美文网首页
[Tutorial]核心代码

[Tutorial]核心代码

作者: Jayzen | 来源:发表于2016-02-01 19:53 被阅读122次
    • 1.用户模型
    • 2.注册
    • 3.登录和退出
    • 4.更新、显示和删除用户
    • 5.账户激活和密码重设
    • 6.用户的微博
    • 7.关注用户
    1. 用户模型

    添加bcrypt这个gem到gemfile文件中:

    gem 'bcrypt'
    

    添加三个迁移文件:

    rails g model User name:string email:string
    rails g migration add_index_to_users_email
    rails g migration add_password_digest_to_users password_digest:string
    (其中第二个文件的内容为:add_index :users, :email, unique: true)
    (其中第三个文件的内容为:add_column :users, :password_digest, :string)
    

    在模型文件中添加的代码如下:

    before_save { self.email = email.downcase }
    validates :name, presence: true, length: { maximum: 50 } 
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
    validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
    has_secure_password
    validates :password, presence: true, length: { minimum: 6 }
    
    2. 注册

    建立资源和单个路由

    resources :users
    match "/signup", to: "users#new", via: "get"
    

    建立users_controller.rb文件

    #使用controller支架
    rails g controller users
    
    class UsersController < ApplicationController
      def new
        @user = User.new
      end
    
      def create
        @user = User.new(user_params)
        if @user.save
          redirect_to user_path(@user)
        else
          render 'new'
        end
      end
    
      def show
        @user = User.find(params[:id])
      end
    
      private
      def user_params
        params.require(:user).permit(:name, :email, :password, :password_confirmation)
      end
    end
    

    在views/users文件中建立显示new.html.erb和show.html.erb文件
    其中new.html.erb文件显示为如下所示:

    <% if @user.errors.any? %>
      <ul>
        <% @user.errors.full_messages.each do |msg| %>
          <li>
            <%= msg %>
          </li>
        <% end %>
      </ul>
    <% end %>
    
    <%= form_for :user, url: users_path do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %></br>
      <%= f.label :email %>
      <%= f.text_field :email %></br>
      <%= f.label :password %>
      <%= f.password_field :password %></br>
      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation %></br>
      <%= f.submit %>
    <% end %>
    

    其中show.html.erb文件显示为如下所示:

    <%= @user.name %></br>
    <%= @user.email %></br>
    <%= link_to "new", new_user_path%>
    
    3. 登录和退出

    三种方式实现登录和退出,分别是浏览器关闭后自动退出,自动记住登录状态,勾选“记住我”选项时才记住用户的登录状态。

    • 第一种方式:浏览器关闭后自动退出

    生成session controller

    rails g controller Sessions new
    

    设置routes.rb

    get 'login' => 'sessions#new'
    post 'login' => 'sessions#create'
    delete 'logout' => 'sessions#destroy'
    

    在sessions_helper.rb文件中建立SessionsHelper模块,并且将该模块导入到application_controller.rb文件中,具体方式通过如下的代码添加到application_controller.rb中:

    include SessionsHelper
    

    SessionsHelper这个模块中的代码具体如下所示:

    module SessionsHelper
      def log_in(user)
        session[:user_id] = user.id
      end
    
      def current_user
        @current_user ||= User.find_by(id: session[:user_id])
      end
    
      def logged_in?
        !current_user.nil?
      end
    
      def log_out
        session.delete(:user_id)
        @current_user = nil
      end
    end
    

    生成的sessions_controller.rb文件中的代码如下所示:

    class SessionsController < ApplicationController
      def new
      end
    
      def create
        user = User.find_by(email: params[:session][:email].downcase)
        if user && user.authenticate(params[:session][:password])
          log_in user
          redirect_to user_path(user)
        else
          render 'new'
        end
      end
    
      def destroy
        log_out
        redirect_to root_path
      end
    end
    

    另外,在users_controller.rb文件中的create这个action中,也要添加如下的语句:

    log_in user
    

    在sessions文件夹中的new.html.erb中添加如下代码:

    <%= form_for(:session, url: login_path) do |f| %>
      <%= f.label :email %>
      <%= f.email_field :email%></br>
      <%= f.label :password %>
      <%= f.password_field :password %></br>
      <%= f.submit %>
    <% end %>
    

    最后通过在在标题中显示代码以标识是否登录,其中header.html.erb这个文件中的代码如下所示:

    <% if logged_in? %>
      <%= link_to "退出", logout_path, method: :delete, data: {confirm: "are you sure?"}%>
    <% else %>
      <%= link_to "登录", login_path %>
      <%= link_to "注册", signup_path %>
    <% end%>
    
    • 第二种方式:自动记住登录状态

    在用户登录这个部分,has_secure_password这个方法,实现的原理是用户输入明码,但是数据库存入的被加密的明码,如果要实现登录,就要对用户的明码进行加密,并与数据库中被加密的明码进行比较,如果相等,就能登录成功,具体代码如下所示:

     user = User.find_by(email: params[:session][:email].downcase)
     if user && user.authenticate(params[:session][:password])
    

    记住用户的登录状态也是使用同样的原理:

    • 生成随机字符串,当做记忆令牌;
    • 把这个令牌存入浏览器的 cookie 中,并把过期时间设为未来的某个日期;
    • 在数据库中存储令牌的摘要;
    • 在浏览器的 cookie 中存储加密后的用户 ID;
    • 如果 cookie 中有用户的 ID,就用这个 ID 在数据库中查找用户,并且检查 cookie 中的记忆令牌和数据库中的哈希摘要是否匹配。

    在上面的ID就类似于用户登录过程中的email,随机字符串类似于用户登录过程中的明码
    具体代码如下所示:
    添加用户所需的remember_digest到用户模型中

    rails g migration add_remember_digest_to_users remember_digest:string
    

    生成一个随机令牌和指定字符串的哈希摘要(类似于用户明码和加密后的用户明码)

    def User.new_token
      SecureRandom.urlsafe_base64
    end
    
    def User.digest(string)
      cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
      BCrypt::Password.create(string, cost: cost)
    end
    

    使用一个虚拟属性remember_token来对随机令牌和指定字符串的哈希摘要进行关联:

    class User < ActiveRecord::Base
      attr_accessor :remember_token
    
      def remember
        self.remember_token = User.new_token
        update_attribute(:remember_digest, User.digest(remember_token))
      end
    end
    

    指定的令牌和hash摘要是否匹配:

    def authenticated?(remember_token)
      BCrypt::Password.new(remember_digest).is_password?(remember_token)
    end
    

    将记忆令牌存入到浏览器的cookie

    cookies.permanent[:remember_token] = remember_token
    

    在浏览器的cookie中存储加密后的用户ID

    cookies.permanent.signed[:user_id] = user.id
    

    通过用户解密后的用户ID查找用户

    User.find_by(id: cookies.signed[:user_id])
    

    在sessions_helper.rb中添加如上提到的功能代码:

    def remember(user)
      user.remember
      cookies.permanent.signed[:user_id] = user.id
      cookies.permanent[:remember_token] = user.remember_token
    end
    #同时在用户登录和用户注册的代码中同时添加语句remember user
    
    def current_user
      if (user_id = session[:user_id]) 
        @current_user ||= User.find_by(id: user_id) 
      elsif (user_id = cookies.signed[:user_id])
        user = User.find_by(id: user_id) 
        if user && user.authenticated?(cookies[:remember_token])    
          log_in user 
          @current_user = user 
        end
      end 
    end
    

    忘记用户:清除remember_digest, cookies
    代码如下所示:

    #user.rb文件中添加forget方法
    def forget
      update_attribute(:remember_digest, nil)
    end
    
    #在sessions_helper.rb中添加forget(user)方法
    def forget(user)
      user.forget
      cookies.delete(:user_id)
      cookies.delete(:remember_token)
    end
    
    #在sessions_helper.rb中的log_out方法中添加forget语句
    def log_out
      forget(current_user)
      sessions.delete(:user_id)
      @current_user = nil
    end
    

    解决两个问题

    • 第一个问题: 在同一个浏览器中存在多个登录页面,当其中一个页面退出的时候,current_user为nil,然后点击另外一个页面的退出按钮,则会报错,nilclass.
    user.forget #=>user为nil,因此不存在此方法
    

    解决的方法是添加如下代码:

    #在sessions_controller.rb中的destroy这个action中添加 if logged_in?
    def destroy
      log_out if logged_in?
      redirect_to root_path
    end
    

    上面的代码logged_in?语句在用户退出的时候起了作用,当用户退出之后,则logged_in?返回为false,则logout_out这条语句不被执行,直接执行下面的语句

    • 第二个问题:在两个浏览器中,如果其中一个浏览器退出,则会将数据库中remember_digest值设置为nil值,那么其他的浏览器在调用remember_digest值的过程中会报错,报错的语句出现的如下代码中:
    def authenticated?(remember_token)
      BCrypt::Password.new(remember_digest).is_password?(remember_token)
    end
    

    修正的方式如下所示:

    def authenticated?(remember_token) 
      return false if remember_digest.nil?
      BCrypt::Password.new(remember_digest).is_password?(remember_token) 
    end
    

    上面的代码在remember_digest为nil的情况下,后面的代码将被忽略。

    • 第三种方式:使用记住我复选框
      在sessions/new.html.erb文件中添加复选框,代码如下所示:
    <%= f.lable :remember_me do %>
      <%= f.check_box :remember_me %>
      <span>remember me on this computer </span>
    <% end %>
    

    在controllers/sessions_controller.rb文件中添加如下代码:

    def create
      params[:session][:remember_me]  == '1' ? remember(user) : forget(user)
    end
    
    3. 更新、显示和删除用户

    更新用户,UsersController.rb文件中的代码内容如下所示:

    def edit
      @user = User.find(params[:id])
    end
    
    def update
      @user = User.find(params[:id])
      if @user.update_attributes(user_params)
        redirect_to @user
      else
        render 'edit'
      end
    end
    

    users/edit.html.erb文件代码内容如下所示:

    <% if @user.errors.any? %>
      <ul>
        <% @user.errors.full_messages.each do |msg| %>
          <%= msg %>
        <% end %>
      </ul>
    <% end %>
    
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %></br>
      <%= f.label :email %>
      <%= f.text_field :email %></br>
      <%= f.label :password %>
      <%= f.password_field :password %></br>
      <%= f.label :password %>
      <%= f.password_field :password %></br>
      <%= f.submit %>
    <% end %>
    

    在_header.html.erb文件中如下所示:

    <%= link_to "编辑", edit_user_path(current_user) %>
    

    只有登录的用户才能编辑
    在UsersController.rb中添加如下代码:

    before_action :logged_in_user, only: [:edit, :update]
    
    def logged_in_user
      unless logged_in?
        redirect_to login_url
      end
    end
    

    只有用户自己才能编辑自己
    在SessionsHelper.rb中添加如下代码:

    def current_user?(user)
      user == current_user
    end
    

    在UsersController.rb中添加如下代码:

    before_action :correct_user, only: [:edit, :update]
    
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end
    

    友好的转向
    在SessionHelper.rb中添加如下代码:

    def redirect_back_or(default)
      redirect_to(session[:forwarding_url] || default)
      session.delete(:forwarding_url)
    end
    
    def store_location
      session[:forwarding_url] = request.url if request.get?
    end
    

    在users_controller.rb文件中添加store_location:

    def logged_in_user
      unless logged_in? 
        store_location 
        redirect_to login_url 
      end 
    end
    

    在sessions_controller.rb文件中添加如下代码:

    def create 
      user = User.find_by(email: params[:session][:email].downcase) 
      if user && user.authenticate(params[:session][:password])   
        log_in user 
        params[:session][:remember_me] == '1' ? remember(user) : forget(user) 
        redirect_back_or user 
      else  
        render 'new' 
      end 
    end
    

    显示所有用户
    在UsersController.rb文件中添加如下的代码:

    def index
      @users = User.all
    end
    

    需要登录之后的用户才能看见全部的用户,UsersController.rb文件中添加如下所示:

    before_action :logged_in_user, only: [:index, :edit, :update]
    

    在_header.html.erb文件中添加如下代码:

    <%= link_to "用户", users_path %>
    

    添加管理员权限

    #终端执行下面代码
    rails g migration add_admin_to_users admin:boolean
    
    #编辑此代码
    def change
      add_column :users, :admin, :boolean, default: false
    end
    

    为用户添加管理员权限的三种方法:

    #终端使用用户创建方法
    user = User.create(name: "demo", email: "demo@foxmail.com", password_confirmation: "111111", password: "111111", admin: true )
    #在db/seeds.rb文件中添加如上的代码
    #在终端中使用toggle方法
    user = User.first
    user.toggle!(:admin)
    

    删除用户
    user_controller.rb文件中添加如下代码

    before_action :logged_in_user, only: [:index,:edit, :update, :destroy]
    before_action :admin_user, only: :destroy
    
    def destroy
      User.find(params[:id]).destroy
      redirect_to users_path
    end
    
    private
    
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
    

    在users/index.html.erb文件中添加如下代码:

    <% if current_user.admin? && !current_user?(user) %>
      <%= link_to "delete", user, method: :delete, data: {confirm: "are you sure?"}%>
    <% end %>
    
    5.账户激活和密码重设
    5.1账户激活

    账户激活的原理和注册用户以及记住用户的原理差不多

    • 用户一开始处于“未激活”状态
    • 用户注册后,生成一个激活令牌和对象的激活摘要
    • 把激活摘要存储在数据库中,然后给用户发送一封邮件,提供包括激活令牌和用户电子邮件地址的链接
    • 用户点击这个链接之后,使用电子邮件地址查找用户,并且对比令牌和摘要
    • 如果令牌和摘要匹配,就把状态由“未激活”改为“已激活”

    使用控制器生成命令:

    rails g controller AccountActivations --no-test-framework
    

    生成所需的资源路由:

    resources :account_activations, only: [:edit]
    

    生成model

    rails g migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime
    

    生成的迁移文件如下所示:

    class AddActivationToUsers < ActiveRecord::Migration
      def change
        add_column :users, :activation_digest, :string
        add_column :users, activated, :boolean, default: false
        add_column :users, :activated_at, :datetime
      end
    end
    

    在model中更新如下方法:

    class User < ActiveRecord::Base
      attr_accessor :remember_token, :activation_token
      before_save :downcase_email
      before_create :create_activation_digest
    
      private
        def downcase_email
          self.email = email.downcase
        end
    
        def create_activation_digest
          self.activation_token = User.new_token
          self.activation_digest = User.digest(activation_token)
        end
    end
    
    5.2 使用邮件程序

    生成mailer的controller文件

    rails generate mailer UserMailer account_activation password_reset
    

    更改controller文件

    def account_activation
      mail(to: "zheng_jiajun@foxmail.com", subject: "welcome to my site")
    end
    

    对config/environments/development.rb文件进行配置

    config.action_mailer.delivery_method = :smtp
    config.action_mailer.smtp_settings = {
        address: "smtp.gmail.com",
        port: 587,
        user_name: "zhengjiajun121",
        password: "xxxx",
        authentication: "plain",
        enable_starttls_auto: true
      }
    

    对上面的文件进行测试

    需要开通VPN
    #rails console
    irb: UserMailer.account_activation.deliver_now
    
    5.3 实现通用的authenticated?方法
    #models/user.rb
    def authenticated?(attribute, token) 
      digest = self.send("#{attribute}_digest") 
      return false if digest.nil? 
      BCrypt::Password.new(digest).is_password?(token)
    end
    
    #更改sessions_helper.rb文件
    def current_user
      if user && user.authenticated?(:remember, cookies[:remember_token])
    end
    
    5.4 账户激活和发送邮件
    #激活账户的controllerapp/controllers/account_activations_controller.rb
    class AccountActivationsController < ApplicationController
      def edit
        user = User.find_by(email: params[:email])
        if user && !user.activated? && user.authenticated?(:activation, params[:id])
          user.update_attribute(:activated, true)
          user.update_attribute(:activated_at, Time.zone.now)
          log_in user
          flash[:notice] = "account activated"
          redirect_to user
        else
          flash[:notive] = "invalid activation link"
          redirect_to root_url
        end
      end
    end
    
    
    class User < ActiveRecord::Base
    #抽象出激活方法
      def activate 
        update_attribute(:activated, true)
        update_attribute(:activated_at, Time.zone.now) 
      end
    
    #发送邮件
      def send_activation_email
        UserMailer.account_activation.deliver_now 
      end
    end
    
    #app/controllers/users_controller.rb文件中
    class UsersController < ApplicationController
      def create @user = User.new(user_params) 
        if @user.save
          #抽象出的发送邮件方法 
          @user.send_activation_email 
          flash[:info] = "Please check your email to activate your account." 
          redirect_to root_url 
        else 
          render 'new' 
        end 
      end
    end
    
    #app/controllers/account_activations_controller.rb
    class AccountActivationsController < ApplicationController 
      def edit 
        user = User.find_by(email: params[:email]) 
        if user && !user.activated? && user.authenticated?(:activation, params[:id]) 
          #抽象出的激活方法
          user.activate 
          log_in user
          flash[:notice] = "account activated" 
          redirect_to user 
        else 
          flash[:notice] = "invalid activation link" 
          redirect_to root_url 
        end 
      end
    end
    
    5.5 显示邮件内容
    #编辑user_mailer.rb
    def account_activation(user)
       @user = user
       mail(to: user.email, subject: "account activation")
    end
    
    #编辑account_activation.html.erb
    <%= link_to "activate", edit_account_activation_url(@user.activation_token, email: @user.email) %>
    
    #编辑account_activation.text.erb
    <%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
    
    class User < ActiveRecord::Base
    #添加self
      def send_activation_email
        UserMailer.account_activation(self).deliver_now 
      end
    end
    
    #编辑config/environments/development.rb,添加host地址
    host = "localhost:3000" 
    config.action_mailer.default_url_options = { host: host }
    
    5.6 不让未激活的用户登录
    #编辑sessions_controller.rb文件内容
    def create
      user = User.find_by(email: params[:session][:email].downcase)
        if user && user.authenticate(params[:session][:password])
          if user.activated?
            log_in user
            params[:session][:remember_me] == '1' ? remember(user) : forget(user)
            redirect_back_or user
          else
            flash[:notice] = "the accout is exist, but not activated"
            redirect_to new_user_url
          end
        else
          render 'new'
        end
      end
    end
    
    5.7 密码重设
    #生成资源控制器
    rails generate controller PasswordResets new edit
    
    #生成路由
    config/route.rb
    Rails.application.routes.draw do 
      resources :password_resets, only: [:new, :create, :edit, :update]
    end
    
    #app/views/sessions/new.html.erb中添加忘记密码链接
    <%= link_to "(forgot password)", new_password_reset_path %>
    
    #生成model
    rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
    
    #设置密码页面app/views/password_resets/new.html.erb
    <%= form_for(:password_reset, url: password_resets_path) do |f| %> 
      <%= f.label :email %> 
      <%= f.email_field :email%> 
      <%= f.submit "Submit"%> 
    <% end %> 
    
    #app/controllers/password_resets_controller.rb中设置动作
    class PasswordResetsController < ApplicationController 
      def new 
      end 
      
      def create 
        @user = User.find_by(email: params[:password_reset][:email].downcase) 
        if @user 
          @user.create_reset_digest 
          @user.send_password_reset_email 
          flash[:notice] = "Email sent with password reset instructions" 
          redirect_to root_url 
        else flash.now[:notice] = "Email address not found" 
          render 'new' 
        end 
      end 
    
      def edit 
      end
    end
    #model中设置需要的方法app/models/user.rb
    class User < ActiveRecord::Base 
      attr_accessor :remember_token, :activation_token, :reset_token
    
      def create_reset_digest 
        self.reset_token = User.new_token 
        update_attribute(:reset_digest, User.digest(reset_token))  
        update_attribute(:reset_sent_at, Time.zone.now) 
      end 
     
      def send_password_reset_email 
        UserMailer.password_reset(self).deliver_now 
      end
    end
    
    #改变邮件程序
    #app/mailers/user_mailer.rb
    class UserMailer < ApplicationMailer
      def password_reset(user) 
        @user = user mail to: user.email, subject: "Password reset"   
      end
    end
    #app/views/user_mailer/password_reset.html.erb
    <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %>
    
    #重设密码表单app/views/password_resets/edit.html.erb
    <%= form_for(@user, url: password_reset_path(params[:id])) do |f| %> 
      <%= hidden_field_tag :email, @user.email %> 
      <%= f.label :password %> 
      <%= f.password_field :password, class: 'form-control' %> 
      <%= f.label :password_confirmation, "Confirmation" %> 
      <%= f.password_field :password_confirmation, class: 'form-control' %> 
      <%= f.submit "Update password", class: "btn btn-primary" %>
    <% end %>
    
    #设置app/controllers/password_resets_controller.rb
    class PasswordResetsController < ApplicationController
      before_action :get_user, only: [:edit, :update] 
      before_action :valid_user, only: [:edit, :update] 
      before_action :check_expiration, only: [:edit, :update] 
    
      def new 
      end
    
      def create @user = User.find_by(email: params[:password_reset][:email].downcase) 
        if @user 
          @user.create_reset_digest   
          @user.send_password_reset_email 
          flash[:info] = "Email sent with password reset instructions" 
          redirect_to root_url 
        else 
          flash.now[:danger] = "Email address not found" 
          render 'new' 
        end 
      end
    
      def edit 
      end 
    
      def update 
        if params[:user][:password].empty? 
          @user.errors.add(:password, "can't be empty") 
          render 'edit' 
        elsif 
          @user.update_attributes(user_params) 
          log_in @user 
          flash[:success] = "Password has been reset." 
          redirect_to @user 
        else 
          render 'edit' 
        end 
      end
    
      private 
        def user_params 
          params.require(:user).permit(:password, :password_confirmation) 
        end 
        # 事前过滤器 
        def get_user 
          @user = User.find_by(email: params[:email]) 
        end 
        # 确保是有效用户 
        def valid_user 
          unless (@user && @user.activated? && @user.authenticated?(:reset, params[:id])) 
            redirect_to root_url 
          end 
         end 
    
        # 检查重设令牌是否过期 
        def check_expiration 
          if @user.password_reset_expired? 
            flash[:danger] = "Password reset has expired." 
            redirect_to new_password_reset_url 
          end 
        end
    end
    
    #在app/models/user.rb中定义 password_reset_expired?方法
    class User < ActiveRecord::Base 
      def password_reset_expired? 
        reset_sent_at < 2.hours.ago 
      end
    end
    
    6 用户的微博
    #建立模型
    rails generate model Micropost content:text user:references
    
    #具体示例
    class CreateMicroposts < ActiveRecord::Migration 
      def change 
        create_table :microposts do |t| 
          t.text :content 
          t.references :user, index: true, foreign_key: true
    
          t.timestamps null: false 
        end 
        #按照时间顺序查找某个用户的微博
        add_index :microposts, [:user_id, :created_at] 
       end
     end
    
    #数据验证
    class Micropost < ActiveRecord::Base 
      belongs_to :user 
      validates :user_id, presence: true
      #默认数据按照时间降序排列
      default_scope -> { order(created_at: :desc) }
      validates :content, presence: true, length: { maximum: 140 }
    end
    
    class User < ActiveRecord::Base 
      has_many :microposts, dependent: :destroy
    end
    
    #显示微博
    rails g controller microposts
    
    #app/views/users/show.html.erb
    #下面的语句会寻找/microposts/_micropost.html.erb文件
    <%= render @microposts %>
    
    
    #app/views/microposts/_micropost.html.erb
    <li><%= micropost.content %></li>
    
    #在app/controllers/users_controller.rb中添加实例变量
    class UsersController < ApplicationController
      def show 
        @user = User.find(params[:id]) 
        @microposts = @user.microposts
      end
    end
    
    #添加配置文件
    resources :microposts, only: [:create, :destroy]
    
    #logged_in_user方法移到 ApplicationController
    #app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base 
      # 确保用户已登录 
      def logged_in_user 
        unless logged_in? 
          store_location flash[:danger] = "Please log in." 
          redirect_to login_url 
        end 
      end
    end
    
    #设置app/controllers/microposts_controller.rb
    class MicropostsController < ApplicationController 
      before_action :logged_in_user, only: [:create, :destroy] 
      
      def create 
        @micropost = current_user.microposts.build(micropost_params)
          if @micropost.save 
            flash[:notice] = "Micropost created!" 
            redirect_to root_url 
          else 
          render 'home/index' 
        end
      end 
      
      def destroy 
      end
    
      private 
        def micropost_params   
          params.require(:micropost).permit(:content) 
        end
    end
    
    #下面要实现用户在主页面和用户页面都可以发送微博
    #app/views/static_pages/home.html.erb
    <% if logged_in? %>
      <%= render 'shared/user_info' %> 
      <%= render 'shared/micropost_form' %> 
    <% else %>
      this is the home page
    <% end %>
    
    #app/views/shared/_user_info.html.erb
    <h1><%= current_user.name %></h1>
    <span><%= link_to "view my profile", current_user %></span>
    <span><%= pluralize(current_user.microposts.count, "micropost") %></span>
    
    #app/views/shared/_micropost_form.html.erb
    <%= form_for(@micropost) do |f| %>
      <%= f.text_area :content, placeholder: "Compose new micropost..." %> 
      <%= f.submit "Post", class: "btn btn-primary" %>
    <% end %>
    
    #app/controllers/home_controller.rb中定义index
    class HomeController < ApplicationController 
      def home @micropost = current_user.microposts.build if logged_in? 
      end
    end
    
    #删除微博
    #app/controllers/microposts_controller.rb
    class MicropostsController < ApplicationController
      before_action :logged_in_user, only: [:create, :destroy]   
      before_action :correct_user, only: :destroy
    
      def destroy @micropost.destroy 
        flash[:success] = "Micropost deleted" 
        redirect_to request.referrer || root_url 
      end
    
      private
      
      def correct_user 
        @micropost =current_user.microposts.find_by(id: params[:id]) 
         redirect_to root_url if @micropost.nil? 
      end
    end
    
    #app/views/microposts/_micropost.html.erb
    <% if current_user?(micropost.user) %> 
      <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
    <% end %>
    
    #动态流原型
    #app/models/user.rb
    class User < ActiveRecord::Base
      def feed 
        Micropost.where("user_id = ?", id) 
      end
    end
    
    class HomeController < ApplicationController 
      def home if logged_in? 
        @micropost = current_user.microposts.build 
        @feed_items = current_user.feed 
      end 
    end
    
    #在首页使用feed_items
    #app/views/shared/_feed.html.erb
    <% if @feed_items.any? %> 
    <%= render @feed_items %> 
    <% end %>
    
    #@feed_items中的元素都是 Micropost类的实例。所以,Rails 会在对应资源的视图文件夹中寻找正确的局部视图:
    app/views/microposts/_micropost.html.erb
    
    7.1关系的模型
    #生成模型
    rails generate model Relationship follower_id:integer followed_id:integer
    
    #模型中建立索引
    class CreateRelationships < ActiveRecord::Migration 
      def change 
        create_table :relationships do |t| 
          t.integer :follower_id 
          t.integer :followed_id 
          t.timestamps null: false 
        end 
        
        add_index :relationships, :follower_id 
        add_index :relationships, :followed_id 
        add_index :relationships, [:follower_id, :followed_id], unique: true 
      end
    end
    
    #app/models/relationship.rb中建立用户关系
    class Relationship < ActiveRecord::Base 
      belongs_to :follower, class_name: "User" 
      belongs_to :followed, class_name: "User"
      validates :follower_id, presence: true 
      validates :followed_id, presence: true
    end
    
    #app/models/user.rb中建立用户关系
    #下面的内容可以参考之前的post_tag文章总结,本质上是一样的
    #其中has_many和belongs_to适用于class_name
    #has_many through适用于source
    
    #following是我关注的用户,followers是我关注的用户
    class User < ActiveRecord::Base
      has_many :active_relationships, class_name: "Relationship", 
        foreign_key: "follower_id", 
        dependent: :destroy 
      has_many :following, through: :active_relationships, 
        source: :followed
    
      has_many :passive_relationships, class_name: "Relationship", 
        foreign_key: "followed_id", 
        dependent: :destroy
      has_many :followers, through: :passive_relationships, 
        source: :follower
    end
    
    #app/models/user.rb中定义辅助方法
    class User < ActiveRecord::Base
    # 关注另一个用户 
      def follow(other_user)
        active_relationships.create(followed_id: other_user.id) 
      end 
    # 取消关注另一个用户 
      def unfollow(other_user)
        active_relationships.find_by(followed_id: other_user.id).destroy 
      end
    # 如果当前用户关注了指定的用户,返回 true 
      def following?(other_user) 
        following.include?(other_user) 
      end
    end
    
    7.2 关系视图的常规实现
    #建立路由config/routes.rb
    resources :users do 
      member do 
        get :following, :followers 
      end
    end
    
    resources :relationships, only: [:create, :destroy]
    
    #显示微博数量的局部视图app/views/share/_stats.html.erb
    #下面这段代码在主页和用户界面都会有显示,主页中不存在@user,因此使用current_user,但是用户界面中不存在
    #current_user,因此只能使用@user
    <% @user ||= current_user %>
    <a href="<%= following_user_path(@user) %>">
      <%= @user.following.count %> following 
    </a> 
    
    <a href="<%= followers_user_path(@user) %>">
      <%= @user.followers.count %>followers
    </a>
    
    #在app/views/home/index.html.erb中添加如上内容
    <%= render 'share/stats' %>
    
    #添加取消和关注视图app/views/users/_follow_form.html.erb
    <% unless current_user?(@user) %> 
      <% if current_user.following?(@user) %> 
        <%= render 'unfollow' %>
      <% else %> 
        <%= render 'follow' %> 
       <% end %> 
    <% end %>
    
    #app/views/users/_follow.html.erb添加关注用户界面
    <%= form_for(current_user.active_relationships.build) do |f| %>
      <%= hidden_field_tag :followed_id, @user.id %>
      <%= f.submit "Follow"%>
    <% end %>
    
    #app/views/users/_unfollow.html.erb取消关注页面
    <%=form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete }) do |f| %> 
      <%= f.submit "Unfollow" %>
    <% end %>
    
    #app/controllers/users_controller.rb添加动作
    class UsersController < ApplicationController
      before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
    
      def following
        @user = User.find(params[:id])
        @users = @user.following
        render 'show_follow' 
      end 
    
      def followers 
        @user = User.find(params[:id]) 
        @users = @user.followers
        render 'show_follow' 
      end
    end
    
    #app/controllers/relationships_controller.rb添加动作
    class RelationshipsController < ApplicationController
      before_action :logged_in_user 
      def create 
         user = User.find(params[:followed_id]) 
         current_user.follow(user) 
         redirect_to user 
      end 
    
      def destroy 
        user = Relationship.find(params[:id]).followed 
        current_user.unfollow(user) 
        redirect_to user 
      end
    end
    

    相关文章

      网友评论

          本文标题:[Tutorial]核心代码

          本文链接:https://www.haomeiwen.com/subject/pjvskttx.html