美文网首页
常用Gems学习(蛋人网)

常用Gems学习(蛋人网)

作者: Jayzen | 来源:发表于2017-01-07 14:15 被阅读248次

    1 Sorcery的安装和设置
    2 Sorcery具体使用
    3 Bootstrap和FontAwesome Gems的安装和使用
    4 Sidekiq安装和使用
    5 Sidekiq深入使用以及生产环境注意事项
    6 Unicorn的安装和使用
    7 Capistrano的安装和使用
    8 Capistrano自定义部署任务: Unicorn进程管理自动化
    9 怎样通过一个shell脚本来部署项目
    10 unicorn进程管理之unicorn-worker-killer gem的使用
    11 eventmachine介绍和怎样实现一个并发的多任务进程
    12 使用ruby和eventmachine创建一个生产环境可用的进程任务:reaper
    13 使用sassc-rails来加快scss文件的编译速度

    1 Sorcery的安装和设置
    Sorcery功能提供类似于Devise,地址,devise支持多模型,但是sorcery只是支持单模型,一般情况下,一个模型就可以了,在这个模型中通过字段属性决定模型的角色,相对而言,sorcery是轻量级的,devise是重量级别的。
    安装和查看:

    #安装gem
    gem 'sorcery'
    
    #显示gem的安装地址
    bundle show sorcery
    
    #使用下面方式可以查看多少功能能使用rails g功能
    rails g -h  #显示rails g sorcery:install
    
    #使用下面方式可以查看sorcery:install安装哪些模块
    rails g sorcery:install -h
    
    #使用,默认使用user这个model
    rails g sorcery:install
    
    #默认生成user模型,为模型添加字段就是给user模型添加字段
    rails g migration add_username_to_users username:string
    
    #改变默认的user模型为person模型,后面必须为Person单数形式,数据库中为people复数形式
    rails generate sorcery:install --model Person
    
    #添加相关模块,一般情况添加下面模块就可以了,不过需要下一节的config配置。
    rails g sorcery:install remember_me reset_password user_activation --only-submodules
    

    配置文件/config/initializers/sorcery.rb,这些地方需要配置才行,目的是把激活功能删除,减少配置的复杂度

    user.user_activation_mailer = nil
    user.activation_mailer_disabled = true
    user.prevent_non_active_users_to_login = false
    

    2 Sorcery具体使用
    sorcery只有针对密码的加密功能,但是没有针对密码的校验和密码一致性的校验。执行上面的模块建立方式:

    rails g sorcery:install remember_me reset_password user_activation --only-submodules
    

    config/initializers/sorcery.rb参考如上的配置方式。
    建立model/user.rb文件代码

    class User < ApplicationRecord  
      authenticates_with_sorcery!
      attr_accessor :password, :password_confirmation
    
      validates_presence_of :email, message: "邮箱不能为空"
      validates :email, uniqueness: true
      validates_presence_of :password, message: "密码不能为空", 
        if: :need_validate_password
      validates_presence_of :password_confirmation, message: "密码确认不能为空", 
        if: :need_validate_password
      validates_confirmation_of :password, message: "密码不一致", 
        if: :need_validate_password
      validates_length_of :password, message: "密码最短6位", 
        minimum: 6, 
        if: :need_validate_password
    
      private
      def need_validate_password
        self.new_record? || 
          (!self.password.nil? or !self.password_confirmation.nil?)
      end
    end
    

    1、上面的代码说明,必须在user.rb文件添加authenticates_with_sorcery!方法,这个是sorcery默认必须放在User类中执行的类方法。默认建立的两个虚拟属性是password和password_confirmation两个属性,sorcery中保存的加密字段是crypted_password,sorcery针对解决的是password的加密,而没有针对password验证的过程,所以上面的代码很多是针对密码的验证。
    2、针对need_validate_password的方法的说明,这个方法在第一次见的时候比较迷惑,经过探索,自己的理解如下。正常的web页面中针对密码的更改和针对用户名等一些信息的修改是放在不同的用户界面中,同时password和password_confirmation是虚拟属性(区别于实际属性,更新的情况下会从数据库取值,而虚拟属性不会从数据库中取值),每一次用户属性更新的情况下,如果没有need_validate_password的方法,那么每次都会执行相应的验证操作,而正常的web页面其实密码更新和用户其他信息的更新是分开的。所以体现了need_validate_password方法的必要性。
    3、这个方法说明,关于密码的验证的前提条件,针对新的字段,或者是针对密码进行更新的情况下(密码进行更新意味着密码肯定不为空)。

    关于sorcery方法的说明:sorcery自定义的方法,一般是应用于model层和controller层,但是例外是helper方法,既可以应用于controller层中,也可以应用于view层中。比如如下两个:

    logged_in? 
    current_user
    

    关于注册controller文件users_controller.rb的说明:
    下面的内容分别对应的是上面在刚开始创建的时候建立的模块。

    class UsersController < ApplicationController
      def new
        @user = User.new
      end
    
      #用户注册
      def create
        @user = User.new(params.require(:user)
          .permit(:email, :password, :password_confirmation))
        
        if @user.save
          flash[:notice] = '注册成功,请登录'
          redirect_to new_session_path
        else
          render action: :new
        end
      end
    
      #用户密码修改
      def change_password
        if current_user.valid_password?(params[:old_password])
          current_user.password_confirmation = params[:password_confirmation]
          
          if current_user.change_password!(params[:password])
            render json: {status: 'ok', msg: '密码修改成功'}
          else
            render json: {status: 'error', msg: current_user.errors.messages.values.flatten}
          end
        else
          render json: {status: 'error', msg: '旧密码不正确'}
        end
      end
    
      #用户发送忘记密码邮件,这里需要对邮件进行配置
      def send_forget_password_email
        @user = User.find_by(email: params[:email])
        if @user
          @user.deliver_reset_password_instructions!
          render json: {status: 'ok', msg: "重置密码的链接已发送到上面的邮箱"}
        else
          render json: {status: 'error', msg: '指定邮箱不存在'}
        end
      end
    
      #密码重置
      def reset_password
        @user = User.load_from_reset_password_token(params[:token])
        if @user
          @user.password_confirmation = params[:password_confirmation]
          if @user.change_password!(params[:password])
            render json: {status: 'ok', msg: '密码修改成功'}
          else
            render json: {status: 'error', msg: @user.errors.messages.values.flatten}
          end
        else
          render json: {status: 'error', msg: 'token不正确或者已过期'}
        end
      end
    
    end
    

    关于用户登录controller文件sessions_controller.rb文件的创建

    class SessionsController < ApplicationController
      
      before_action :auth_user, except: [:destroy]
    
      def new
      end
    
      def create
        #login是sorcery的登录方法
        if user = login(params[:email], params[:password])
          flash[:notice] = '登陆成功'
          redirect_to root_path
        else
          flash[:notice] = "邮箱或者密码错误"
          redirect_to new_session_path
        end
      end
    
      def destroy
        #logout是sorcery的退出方法
        logout
        flash[:notice] = "退出登陆"
        redirect_to root_path
      end
    
      private
      def auth_user
        #logged_in?是sorcery的判断登录方法
        if logged_in?
          redirect_to root_path
        end
      end
    
    end
    

    登录界面

    <h1>Sessions#new</h1>
    <%= form_tag sessions_path, method: :post, class: "form-horizontal" do %>
      <div class="form-group">
        <div class="col-lg-12">邮箱</div>
        <div class="col-lg-12">
          <%= text_field_tag :email, nil, class: "form-control" %>
        </div>
      </div>
      <div class="form-group">
        <div class="col-lg-12">密码</div>
        <div class="col-lg-12">
          <%= password_field_tag :password, nil, class: "form-control" %>
        </div>
      </div>
      <div class="form-group">
        <div class="col-lg-12">
          <div class="pull-right">
            <a href="<%= new_user_path %>">注册</a>
          </div>
        </div>
      </div>        
      <div class="form-group">
        <div class="col-lg-12">
          <input type="submit" name="sign_submit" value="登陆" class="col-xs-12 btn btn-primary"> 
        </div>
      </div>
    <% end -%>
    

    注册界面

    <h1>Users#new</h1>
    <div>
      <% unless @user.errors.empty? %>
        <ul>
          <% @user.errors.messages.values.flatten.each do |error| %>
            <li><%= error %></li>
          <% end -%>
        </ul>
      <% end -%>
    </div>
    <%= form_for :user, url: users_path, method: :post, html: { class: 'form-horizontal', id: "user_form"} do |f| %>
      <div class="form-group">
        <div class="col-lg-12">邮箱 *</div>
        <div class="col-lg-12">
          <%= f.text_field :email, class: "form-control" %>
        </div>
      </div>
      <div class="form-group">
        <div class="col-lg-12">密码 *</div>
        <div class="col-lg-12">
          <%= f.password_field :password, class: "form-control" %>
        </div>
      </div>
      <div class="form-group">
        <div class="col-lg-12">密码确认 *</div>  
        <div class="col-lg-12">
          <%= f.password_field :password_confirmation, class: "form-control" %>
        </div>
      </div>
      <div class="form-group">
        <div class="col-lg-12">
          <input type="submit" name="create_user_submit" value="创建账户" class="col-xs-12 btn btn-primary"> 
        </div>
      </div>
    <% end -%>
    

    3 Bootstrap和FontAwesome Gems的安装和使用
    bootstrap的安装和使用在工具篇中已经做了介绍。
    fontawesome是一个图标库。可以被当做类似字体使用。这里介绍fontawesome的安装和使用。

    #gemfile文件
    gem 'font-awesome-rails'
    
    #application.scss #使用scss文件可以使用@import语法
    @import "font-awesome"; #@import语法说明是引入该scss文件
    
    #使用font-awesome
    <i class= "fa fa-gear"></i>
    

    使用响应式布局需要在application.html.erb中添加

     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    

    4 Sidekiq安装和使用
    背景知识1:安装和启动redis(目前稳定版本是3.2.6)

    #安装和编译
    $ wget http://download.redis.io/releases/redis-3.2.7.tar.gz
    $ tar xzf redis-3.2.7.tar.gz
    $ cd redis-3.2.7
    $ make
    
    #启动,在编译之后的src文件夹中执行下面操作
    $ redis-server
    上面这个方式会占用一个终端,通过下面的方式启动redis的同时重新打开一个终端
    redis-server &
    
    #进行交互
    $ src/redis-cli  #开启redis客户端
    redis> set foo bar
    OK
    redis> get foo
    "bar"
    
    #关闭redis,打开redis-cli,输入
    shutdown
    

    sidekiq的配置和使用

    #原理
    web端生成异步任务,然后将这些任务存储在redis数据库中,sidekiq从redis数据库中读取这个异步任务,并且执行。
    
    #gemfile文件
    gem 'sidekiq'
    
    #config/application.rb
    #active_job是rails针对异步操作统一规定的interface
    config.active_job.queue_adapter = :sidekiq
    
    #开启redis数据库
    redis-server #需要在redis的src文件目录下,sidekiq默认连接的是redis数据库
    redis-client shutdown #关闭redis数据库,或者CTRL+C
    
    #开启sidekiq服务
    sidekiq -C config/sidekiq.yml -e development
    sidekiq -C config/sidekiq.yml -e development -d #运行放在后台
    
    #sidekiq的配置文件config/sidekiq.yml
    :concurrency: 5
    :pidfile: tmp/pids/sidekiq.pid
    development:
      :concurrency: 2
    production:
      :concurrency: 5
    :queues:
      - default
      - [mailers, 2]
    :logfile: log/sidekiq.log
    
    #对应config/sidekiq.yml文件建立sidekiq.pid文件
    tmp/pids/sidekiq.yml  #参照目录结构建立空文件
    
    
    
    #通过rails g job异步框架以及相应的代码形式
    1.默认形式
    rails g job guests_cleanup
    class GuestsCleanupJob < ApplicationJob
      #:default显示的是使用默认队列,队列的优先级和名称可以设置
      queue_as :default
     
      def perform(*guests)
        # Do something later
      end
    end
    2.可以实现设置队列的名称
    rails g job guests_cleanup  --queue urgent
    #显示代码,省略形式
    queue_as :urgent
    
    
    #执行异步任务,前提是启动redis和sidekiq
    参考上面已经建立的队列,在rails c平台下面进行执行:
    GuestsCleanupJob.perform_later("abc") 
    执行结果如下:
    <GuestsCleanupJob:0x007fbd0c589230 @arguments=["abc"], 
    @job_id="830cd076-33dc-4825-9238-37b94f607bcc", 
    @queue_name="default", @priority=nil, 
    @provider_job_id="9797351c-c8a2-4c51-a42f-b6822bdfbbc4">
    立即执行如下结果:
    Performing GuestsCleanupJob from Async(default) with arguments: "abc" Performed GuestsCleanupJob from Async(default) in 11.41ms
    上面的结果会立马执行,通过下面的方式会延时执行
    GuestsCleanupJob.set(wait: 5.minutes).perform_later("abc") 
    
    #通过ui界面查看异步任务,需要编辑config/routes.rb文件
    require 'sidekiq/web'
    mount Sidekiq::Web => '/sidekiq-stat'
    #通过如下的链接访问sidekiq界面
    http://0.0.0.0:3000/sidekiq-stat
    
    #需要注意的问题
    1. sidekiq是不会重载的,如果建立了新的队列,需要重启sidekiq
    

    使用sidekiq来发送邮件
    第一部分介绍发送邮件部分,之前的总结中已经存在需要更新的地方

    #与controller的使用规则类似,在终端运行下面的代码
    rails g mailer UserMailer
    
    #在user_mailer.rb文件中建立如下的代码:
    class ApplicationMailer < ActionMailer::Base
      default from: "zhengjiajun121@gmail.com"
     
      def welcome_email
        mail(to: "zheng_jiajun@foxmail.com", subject: "Welcome to My Awesome Site")
      end
    end
    
    #app/views/user_mailer下面建立两个视图文件,分别是welcome_email.html.erb文件和welcome_email.text.erb文件:
    
    #进行文件配置,在config/environments/developments.rb中添加如下代码:
    
    config.action_mailer.delivery_method = :smtp                  
    config.action_mailer.smtp_settings = {                                   
      address: "smtp.gmail.com",                                             
      port: 587,                                                             
      user_name: "zhengjiajun121",                                           
      password: "xxx",                                                 
      authentication: "plain",                                               
      enable_starttls_auto: true } 
    
    #在终端执行下面的文件,邮件可以从gmail邮箱发送至foxmail邮箱中:
    UserMailer.welcome_email.deliver_now
    

    第二部分发送邮件结合sidekiq和active_job

    class GuestsCleanupJob < ApplicationJob
      #:default显示的是使用默认队列,队列的优先级和名称可以设置
      queue_as :default
    
      def perform(*guests)
        UserMailer.welcome_email.deliver_now 
      end
    end
    
    #终端执行下面代码
    GuestsCleanupJob.set(wait: 5.minutes).perform_later
    

    5 Sidekiq深入使用以及生产环境注意事项
    1.异常处理

    6 Unicorn的安装和使用
    passenger,unicorn是基于进程的模式,puma是基于线程的模式。passenger和unicorn的区别在于前者有企业服务,运维比较方便,后者是完全开源的。

    #添加的gem
    gem 'unicorn'
    
    #添加unicorn的配置文件config/unicorn.rb,注释部分是nginx的配置文件
    # http://unicorn.bogomips.org/
    # startup:
    #   unicorn_rails -E production -p 8081 -c config/unicorn.rb -D
    #
    
    # ngxin.conf
    #
    # user nginx;
    # worker_processes 2;
    # error_log /var/log/nginx/error.log;
    # pid /run/nginx.pid;
    
    # events {
    #     worker_connections 1024;
    # }
    
    # http {
    #     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                       '$status $body_bytes_sent "$http_referer" '
    #                       '"$http_user_agent" "$http_x_forwarded_for"';
    
    #     access_log  /var/log/nginx/access.log  main;
    
    #     sendfile            on;
    #     tcp_nopush          on;
    #     tcp_nodelay         on;
    #     keepalive_timeout   30;
    #     types_hash_max_size 2048;
    
    #     include             /etc/nginx/mime.types;
    #     default_type        application/octet-stream;
    
    
    #     underscores_in_headers    on;
    
    #     # Load modular configuration files from the /etc/nginx/conf.d directory.
    #     # See http://nginx.org/en/docs/ngx_core_module.html#include
    #     # for more information.
    #     include /etc/nginx/conf.d/*.conf;
    
    #     gzip  on;
    #     gzip_http_version 1.1;
    #     gzip_vary on;
    #     gzip_comp_level 5;
    #     gzip_types text/css text/html application/x-javascript application/javascript text/javascript application/json;
    #     gzip_buffers 16 8k;
    
    #     client_max_body_size 20m;
    
    #     upstream app {
    #         server localhost:3000 fail_timeout=0s;
    #     }
    
    #     server {
    #        listen 80;
    #        server_name doman.com;
    
    #         error_page   500 502 503 504  /50x.html;
    #         location = /50x.html {
    #             root   html;
    #         }
    
    #         root /mnt/www/app/current/public;
    
    #         # try_files will find the $uri in the given file list, if not found it will hand it to @app handler
    #         try_files $uri/index.html $uri @app; 
    
    #         location ~* \.(xml|txt|html|htm|ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
    #             expires max;
    #             try_files $uri/index.html $uri @app; 
    #         }
    
    #         location @app {
    #             proxy_set_header   Host $host;
    #             proxy_set_header   X-Forwarded-Host $host;
    #             proxy_set_header   X-Forwarded-Server $host;
    #             proxy_set_header   X-Real-IP        $remote_addr;
    #             proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    #             proxy_buffering    on;
    #             proxy_pass         http://app;
    #        }
    #     }
    # }
    
    
    rails_env = ENV["RAILS_ENV"] || "development"
    rails_root = File.expand_path(__FILE__).split('/')[0..-3].join('/')
    
    port_number = 3000
    process_number = rails_env == 'production' ? 2 : 1
    
    puts "unicorn env: #{rails_env}"
    puts "unicorn port: #{port_number}"
    puts "unicorn process number: #{process_number}"
    
    preload_app true
    working_directory rails_root
    pid "#{rails_root}/tmp/pids/unicorn.pid"
    stderr_path "#{rails_root}/log/unicorn.log"
    stdout_path "#{rails_root}/log/unicorn.log"
    
    listen port_number, :tcp_nopush => true
    
    # listen "/tmp/unicorn.ruby-china.sock"
    worker_processes process_number
    timeout 30
    
    before_fork do |server, worker|
      # the following is highly recomended for Rails + "preload_app true"
      # as there's no need for the master process to hold a connection
      #
      # LL NOTE:
      # => master node will not serve http request, so it's no use to hold the database connection.
      # => https://devcenter.heroku.com/articles/concurrency-and-database-connections
      #
      if defined?(ActiveRecord::Base)
        ActiveRecord::Base.connection.disconnect!
      end
    end
    
    after_fork do |server, worker|
      if defined?(ActiveRecord::Base)
        ActiveRecord::Base.establish_connection
      end
    end
    
    #启动
    unicorn_rails -E production -p 8081 -c config/unicorn.rb -D
    
    

    7 Capistrano的安装和使用
    13 使用sassc-rails来加快scss文件的编译速度

    相关文章

      网友评论

          本文标题:常用Gems学习(蛋人网)

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