常用Gems学习(蛋人网)

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只有针对密码的加密功能,但是没有针对密码的校验和密码一致性的校验。执行上面的??榻⒎绞剑?/p>

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文件的编译速度

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容