<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>bajiudongfeng (bajiudongfeng)</title>
    <link>https://ruby-china.org/bajiudongfeng</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Missing `secret_key_base` for 'production' environment  </title>
      <description>&lt;p&gt;错误如上，但是我已经设置了 SECRET_KEY_BASE&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo $SECRET_KEY_BASE

6f1cdd7d6de9cc27b3d03a296b7637f93bc7d362d4223527d4777ee1ce968614c142f2feb7bc30668ddca56879dcf5ed7e7bdfef7aee83ab5eb868cd33b4346d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为啥依然会报这个错误呢？&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Tue, 31 Oct 2017 15:48:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/34479</link>
      <guid>https://ruby-china.org/topics/34479</guid>
    </item>
    <item>
      <title>gem mina 的学习</title>
      <description>&lt;p&gt;有些地方理解的还不是很到位，还请大家指点．&lt;/p&gt;
&lt;h3 id="1. 概述"&gt;1. 概述&lt;/h3&gt;
&lt;p&gt;mina 是一个快速部署工具。原理：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;将部署过程需要用到的所有命令放置到数组之中&lt;/li&gt;
&lt;li&gt;转换成对应的字符串&lt;/li&gt;
&lt;li&gt;登录远程服务器，执行命令&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2. 深入分析"&gt;2. 深入分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;mina 主体代码结构：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/93e904a6-1461-47b5-8386-0efa6cfab49d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;各个文件作用简述：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Mina::Application&lt;/code&gt;  类
主要是初始化 mina，执行队列中的命令.
主要方法 &lt;code&gt;run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Backend::Local&lt;/code&gt; 和 &lt;code&gt;Mina::Backend::Remote&lt;/code&gt;  类
一个是在当前机器执行命令，一个是在远程服务器执行命令。
关键方法：&lt;code&gt;ssh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Helpers::Internal&lt;/code&gt;  模块
主要是对命令进行一些字符串方面的处理，记录部署花费时间等.&amp;lt;br＞
&lt;code&gt;Mina::Helpers::Output&lt;/code&gt;  模块
主要是输出的操作，用不同的颜色表示不同信息的输出。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Runner::Exec&lt;/code&gt; 
&lt;code&gt;Mina::Runner::Pretty&lt;/code&gt; 
&lt;code&gt;Mina::Runner::Printer&lt;/code&gt; 
&lt;code&gt;Mina::Runner::System&lt;/code&gt; 
这几个类实现了 ruby 调用 shell 的几种形式。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Commands&lt;/code&gt;  类
定义了对命令的一些基本操作.
例如:
方法：&lt;code&gt;command&lt;/code&gt;  将命令加入队列
方法：&lt;code&gt;process&lt;/code&gt; 将命令的队列转换成字符串
方法：&lt;code&gt;run&lt;/code&gt; 执行命令&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Configuration&lt;/code&gt; 类
定义了 mina 中最基本的方法
例如:
&lt;code&gt;set&lt;/code&gt; 
&lt;code&gt;fetch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::DSL&lt;/code&gt; 模块
定义了一些关键的，会直接使用到的方法
例如：
方法：&lt;code&gt;invoke&lt;/code&gt; 让任务可以被调用
方法：&lt;code&gt;in_path&lt;/code&gt; 在某个目录下运行命令
方法：&lt;code&gt;run&lt;/code&gt; 执行某个命令&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mina::Runner&lt;/code&gt; 类
主要是命令的最后执行．&lt;/p&gt;
&lt;h3 id="3. 举例说明运行流程"&gt;3. 举例说明运行流程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;例子：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;mina run["ls -l"]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mina 任务源码（&lt;code&gt;tasks/mina/default.rb&lt;/code&gt;）
接下来分析下 mina 运行这个命令的流程
mina 定义了运行命令的任务&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;desc 'Runs a command in the server.'
task :run, [:command] do |_, args|
  ensure!(:deploy_to)
  command = args[:command]

  unless command
    puts "You need to provide a command. Try: mina 'run[ls -la]'"
    exit 1
  end

  in_path fetch(:deploy_to) do
    command command
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;起点和终点&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所有的 &lt;code&gt;mina&lt;/code&gt; 任务的运行都要先执行文件 &lt;code&gt;bin/mina&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;#!/usr/bin/env ruby&lt;/code&gt;
&lt;code&gt;require 'mina'&lt;/code&gt;
&lt;code&gt;Mina::Application.new.run&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;初始化 mina，同时开始执行命令&lt;/p&gt;

&lt;p&gt;我们来看这个过程都发生了那些操作，关键点：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def run
  Rake.application = self
  super
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;将 Mina::Application 初始化对象复制给 Rake.application&lt;/li&gt;
&lt;li&gt;调用父类 Rake::Application 的 run 方法，这个会导致对方法&lt;code&gt;top_level_tasks&lt;/code&gt;调用。其中很关键的是：  &lt;code&gt;:run_commands&lt;/code&gt;．这个会导致对 run_commands 这个任务的执行（稍后会提到）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;之后会回到任务&lt;code&gt;mina run["ls -l"]&lt;/code&gt; 之中&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ensure!(:deploy_to)
command = args[:command]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认部署目录有值，获取参数&lt;/p&gt;

&lt;p&gt;核心来了&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;in_path fetch(:deploy_to) do
    command command
 end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来看 in_path 方法：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def in_path(path, indent: nil)
  real_commands = commands
  @commands = Commands.new
  yield
  real_commands.command(commands.process(path), quiet: true, indent: indent)
  @commands = real_commands
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;yield&lt;/code&gt;中的操作也就是&lt;code&gt;command command&lt;/code&gt; 
这是将命令&lt;code&gt;ls -l&lt;/code&gt;放入队列之中．&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def initialize(stage = :default)
  @stage = stage
  @queue = Hash.new { |hash, key| hash[key] = [] }
end

def command(code, strip: true, quiet: false, indent: nil)
  code = unindent(code) if strip
  code = indent(indent, code) if indent
  queue[stage] &amp;lt;&amp;lt; (quiet ? code : echo_cmd(code))
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对命令进行一些简单的处理，然后加入队列&lt;code&gt;queue[:default]&lt;/code&gt;中．
类似结果：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;#&amp;lt;Mina::Commands:0x0000000291a5d0 @stage=:default, @queue={:default=&amp;gt;["ls -l"]}&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;将命令进行一些处理再次放入队列之中&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def process(path = nil)
  if path
    queue[stage].unshift(%{echo "$ cd #{path}"}) if fetch(:verbose)
    %{(cd #{path} &amp;amp;&amp;amp; #{queue[stage].join(' &amp;amp;&amp;amp; ')} &amp;amp;&amp;amp; cd -)}
  else
    queue[stage].join("\n")
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;类似结果：
＞ &lt;code&gt;#&amp;lt;Mina::Commands:0x0000000291a698 @stage=:default, @queue={:default=&amp;gt;["(cd /home/zhang/dongfeng &amp;amp;&amp;amp; ls -l &amp;amp;&amp;amp; cd -)"]}&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;接下来会执行任务 &lt;code&gt;run_commands&lt;/code&gt;，这个应该还是&lt;code&gt;Mina::Application&lt;/code&gt;的&lt;code&gt;run&lt;/code&gt; 方法中的&lt;code&gt;super&lt;/code&gt;的因素。具体原因还不清楚．&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task :environment do
end

task :run_commands do
  if commands.run_default?
    invoke :environment
    commands.run(:remote)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当 stage == :default 且命令队列不为空的时候才执行.
会调用任务 mina environment，默认为空，但是项目中的配置文件一般会覆写这个任务用来加载&lt;code&gt;ruby&lt;/code&gt;环境，例如&lt;code&gt;rvm&lt;/code&gt;等．&lt;/p&gt;

&lt;p&gt;快到最后时刻了&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def report_time
       time_start = Time.now
       output = yield
       print_info "Elapsed time: %.2f seconds" % [Time.now - time_start]
       output
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义了计算时间的方法．&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
def run(backend)
      return if queue.empty?
      report_time do
        status = Mina::Runner.new(process, backend).run
        error! 'Run Error' unless status
      end
 end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;命令的执行即将开始．
关键参数类似结果：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;process :          (cd /home/zhang/dongfeng &amp;amp;&amp;amp; ls -l &amp;amp;&amp;amp; cd -)
backend:  remote&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Mina::Runner&lt;/code&gt;的关键方法：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; def run
      Mina::Runner.const_get(class_name_for(execution_mode)).new(script).run
 end
private

def script
   　Mina::Backend.const_get(class_name_for(backend)).new(commands).prepare
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Mina::Runner.const_get(class_name_for(execution_mode))&lt;/code&gt;
主要就是选用那种执行方式：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Mina::Runner::Exec&lt;/code&gt; 
&lt;code&gt;Mina::Runner::Pretty&lt;/code&gt; 
&lt;code&gt;Mina::Runner::Printer&lt;/code&gt; 
&lt;code&gt;Mina::Runner::System&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而 script 则确定在远程还是在本地执行，此处是在远程执行．&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def prepare
  if fetch(:simulate)
    [
      '#!/usr/bin/env bash', "# Executing the following via '#{ssh}':",
      '#', commands, ' '
    ].join("\n")
  else
    command = Shellwords.escape(commands)
    w = [ssh, '--', command].join(' ')
  end
end

def ssh
  ensure!(:domain)
  args = fetch(:domain)
  args = "#{fetch(:user)}@#{fetch(:domain)}" if set?(:user)
  args += " -i #{fetch(:identity_file)}" if set?(:identity_file)
  args += " -p #{fetch(:port)}" if set?(:port)
  args += ' -A' if set?(:forward_agent)
  args += " #{fetch(:ssh_options)}" if set?(:ssh_options)
  args += ' -tt'
  "ssh #{args}"
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;prepare&lt;/code&gt; 会对命令进行一些处理
类似结果：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(cd\ /home/zhang/dongfeng\ &amp;amp;&amp;amp;\ ls\ -l\ &amp;amp;&amp;amp;\ cd\ -)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;然后把 ssh 远程登录的命令加入到所有命令的最开始处．
类似结果：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ssh user_name@***.xyz -p 22 -tt -- (cd\ /home/zhang/dongfeng\ &amp;amp;&amp;amp;\ ls\ -l\ &amp;amp;&amp;amp;\ cd\ -)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;此时就开始登录远程服务器执行命令了，静待即可．&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Wed, 12 Apr 2017 22:51:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/32767</link>
      <guid>https://ruby-china.org/topics/32767</guid>
    </item>
    <item>
      <title>Devise async send email</title>
      <description>&lt;h4 id="1. 解决方案"&gt;&lt;strong&gt;1. 解决方案&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;在 user 这个 model 下添加如下代码&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Override Devise to send mails with async&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_devise_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;devise_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_later&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认的话使用 devise 内部内置的发送邮件的方法，也可以进行覆盖，自定义邮件发送。但是要注意修改&lt;code&gt;devise.rb&lt;/code&gt;文件：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;# Configure the class responsible to send e-mails.&lt;br&gt;
  # config.mailer = 'Devise::Mailer'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;后台进行邮件处理的可以选择 Resque,sidekiq 等，此处使用&lt;code&gt;sidekiq&lt;/code&gt;
在 application.rb 文件中加入如下代码：&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;config.active_job.queue_adapter = :sidekiq&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;devise 默认的邮件发送使用的 redis 队列名称是 &lt;code&gt;mailers&lt;/code&gt;,因此要在&lt;code&gt;sidekiq.yml&lt;/code&gt;文件中加入此队列。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;- [mailers, 3]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="2. 运行结果"&gt;&lt;strong&gt;2. 运行结果&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;development.log 文件：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[ActiveJob] Enqueued ActionMailer::DeliveryJob (Job ID: 6a5e3f3f-bccf-4fc7-89e0-f16f63918aa0) to Sidekiq(mailers) with arguments: "Devise::Mailer", "reset_password_instructions", "deliver_now", #&amp;gt;, "CpfoqSDN3z6zvA9yiUsL", {}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;sidekiq.log 文件：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2017-04-09T08:53:57.511Z 24351 TID-y44z8 ActionMailer::DeliveryJob JID-fede8f1837ccca19b5dbf262 INFO: start&lt;br&gt;
2017-04-09T08:53:58.493Z 24351 TID-y44z8 ActionMailer::DeliveryJob JID-fede8f1837ccca19b5dbf262 INFO: done: 0.983 sec&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="3. 源码探究"&gt;&lt;strong&gt;3. 源码探究&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;打印 &lt;code&gt;send_devise_notification(notification, *args)&lt;/code&gt; 参数信息：&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I, [2017-04-09T17:00:56.308408 #21573]  INFO -- :     notification: reset_password_instructions&lt;br&gt;
I, [2017-04-09T17:00:56.308512 #21573]  INFO -- :     args: ["hmNJj3Gszwvswd2VjxQL", {}]&lt;br&gt;
I, [2017-04-09T17:00:56.498526 #21573]  INFO -- :     devise_mailer: Devise::Mailer Class&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因此可知
类：   &lt;code&gt;Devise::Mailer&lt;/code&gt; 
方法：&lt;code&gt;reset_password_instructions&lt;/code&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;源码文件路径：&lt;code&gt;devise/app/mailers/devise/mailer.rb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Devise::Mailer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Mailers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confirmation_instructions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="vi"&gt;@token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
      &lt;span class="n"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:confirmation_instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset_password_instructions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="vi"&gt;@token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
      &lt;span class="n"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reset_password_instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unlock_instructions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="vi"&gt;@token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
      &lt;span class="n"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:unlock_instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;email_changed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="n"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email_changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;password_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="n"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password_change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此：&lt;code&gt;devise_mailer&lt;/code&gt;会根据不同的参数调用不同的方法。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Devise.parent_mailer.constantize&lt;/code&gt; 解析：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# devise/lib/devise.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Devise&lt;/span&gt;
  &lt;span class="c1"&gt;# The parent mailer all Devise mailers inherit from.&lt;/span&gt;
  &lt;span class="c1"&gt;# Defaults to ActionMailer::Base. This should be set early&lt;/span&gt;
  &lt;span class="c1"&gt;# in the initialization process and should be set to a string.&lt;/span&gt;
  &lt;span class="n"&gt;mattr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:parent_mailer&lt;/span&gt;
  &lt;span class="vc"&gt;@@parent_mailer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ActionMailer::Base"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;发送邮件操作进一步解析&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;发送邮件的方法 &lt;code&gt;devise_mail&lt;/code&gt; 定义在 module &lt;code&gt;Devise::Mailers::Helpers&lt;/code&gt;中，这是发送邮件的具体实现。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 发送邮件 record 就是User记录, action 是 :reset_password_instructions&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;devise_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;initialize_from_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mail&lt;/span&gt; &lt;span class="n"&gt;headers_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="c1"&gt;# 一些初始化的工作&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_from_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@scope_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_scope!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@resource&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;instance_variable_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;devise_mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 设置一些头信息&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;headers_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;subject: &lt;/span&gt;&lt;span class="n"&gt;subject_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="n"&gt;mailer_sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devise_mapping&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;reply_to: &lt;/span&gt;&lt;span class="n"&gt;mailer_reply_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devise_mapping&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;template_path: &lt;/span&gt;&lt;span class="n"&gt;template_paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;template_name: &lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;
  &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="vi"&gt;@email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:to&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;headers&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;headers 的结果类似：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;headers: {:subject=&amp;gt;"重置密码信息", :to=&amp;gt;"14172@qq.com", :from=&amp;gt;"infoabc@qq.com", :reply_to=&amp;gt;"infoabc@qq.com", :template_path=&amp;gt;["devise/mailer"], :template_name=&amp;gt;:reset_password_instructions}&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Sun, 09 Apr 2017 18:41:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/32729</link>
      <guid>https://ruby-china.org/topics/32729</guid>
    </item>
    <item>
      <title>What are blocks, procs, and lambdas?</title>
      <description>&lt;p&gt;&lt;a href="http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/" rel="nofollow" target="_blank" title=""&gt;原文链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. 主要区别：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proc 和 Lambda 都是对象，而 Block 不是&lt;/li&gt;
&lt;li&gt;&lt;p&gt;参数列表中最多只能有一个 Block，但是可以有多个 Proc 或 Lambda&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lambda 对参数的检查很严格，而 Proc 则比较宽松&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proc 和 Lambda 中 return 关键字的行为是不同的&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. 特殊符号&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;同时要注意&amp;amp;符号&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The '&amp;amp;' tells ruby to turn the proc into a block&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;例如&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sale.limit(5).map(&amp;amp;:id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;:id&lt;/code&gt;是一个 symbol 对象，当在其前面加上&lt;code&gt;&amp;amp;&lt;/code&gt;就表示后边需要的是一个 proc，因此&lt;code&gt;:id&lt;/code&gt;会调用其 to_sym 方法。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. 具体代码分析&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;基本的例子&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Block Examples
[1,2,3].each { |x| puts x*2 }   # block is in between the curly braces

[1,2,3].each do |x|
  puts x*2                    # block is everything between the do and end
end

# Proc Examples             
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&amp;amp;p)              # The '&amp;amp;' tells ruby to turn the proc into a block 

proc = Proc.new { puts "Hello World" }
proc.call                     # The body of the Proc object gets executed when called

# Lambda Examples            
lam = lambda { |x| puts x*2 }
[1,2,3].each(&amp;amp;lam)

lam = lambda { puts "Hello World" }
lam.call
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;区别 1(Proc 和 Lambda 都是对象，而 Block 不是) 举例&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p = Proc.new { puts "Hello World" }

p.call  # prints 'Hello World'
p.class # returns 'Proc'
a = p   # a now equals p, a Proc instance
p       # returns a proc object '#&amp;lt;Proc:0x007f96b1a60eb0@(irb):46&amp;gt;'

{ puts "Hello World"}       # syntax error  
a = { puts "Hello World"}   # syntax error
[1,2,3].each {|x| puts x*2} #
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;p 是类 Proc 的一个实例对象，其可以调用方法，复制给其他变量，也可以返回自己.
block 是方法调用的一部分，不可以单独存在，&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;区别 2(参数列表中最多只能有一个 Block，但是可以有多个 Proc 或 Lambda) 举例&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def multiple_procs(proc1, proc2,&amp;amp;block)
  proc1.call
  proc2.call
  block.call
end

a = Proc.new { puts "First proc" }
b = Proc.new { puts "Second proc" }

multiple_procs(a,b) {puts "block"}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;区别 3(Lambda 对参数的检查很严格，而 Proc 则比较宽松) 举例&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

proc   # returns '#&amp;lt;Proc:0x007f96b1032d30@(irb):75&amp;gt;'
lam    # returns '&amp;lt;Proc:0x007f96b1b41938@(irb):76 (lambda)&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;proc 和 lam 都是 Proc 的实例对象，仅有微小的不同
参数检查的不同&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2)                    # prints out 2
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3)                # ArgumentError: wrong number of arguments (3 for 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;区别 4(Proc 和 Lambda 中 return 关键字的行为是不同的) 举例&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'


def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个有点类似循环中的&lt;code&gt;continue&lt;/code&gt; 和 &lt;code&gt;break&lt;/code&gt;&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Sat, 08 Apr 2017 20:33:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/32726</link>
      <guid>https://ruby-china.org/topics/32726</guid>
    </item>
    <item>
      <title>devise_async 发送邮件出错</title>
      <description>&lt;ul&gt;
&lt;li&gt;使用 devise_async 发送邮件，按照这个&lt;a href="https://github.com/mperham/sidekiq/wiki/Devise" rel="nofollow" target="_blank" title=""&gt;教程&lt;/a&gt;做的。&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;可是却报如下错误，求解：&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ArgumentError (wrong number of arguments (given 3, expected 1..2)):

app/controllers/users/registrations_controller.rb:13:in `create'
  Rendering /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
  Rendering /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
  Rendered /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (4.9ms)
  Rendering /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
  Rendered /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (3.2ms)
  Rendering /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
  Rendered /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.6ms)
  Rendered /home/zhang/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.2/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (27.7ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Sat, 25 Mar 2017 15:11:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/32633</link>
      <guid>https://ruby-china.org/topics/32633</guid>
    </item>
    <item>
      <title>GitHub 登录 OAuth 学习</title>
      <description>&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;简介 &lt;a href="https://zh.wikipedia.org/wiki/OAuth" rel="nofollow" target="_blank" title=""&gt;维基百科&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OAuth（开放授权）是一个开放标准，允许用户让第三方应用访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方应用。&lt;/p&gt;

&lt;p&gt;OAuth 允许用户提供一个令牌，而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站（例如，视频编辑网站) 在特定的时段（例如，接下来的 2 小时内）内访问特定的资源（例如仅仅是某一相册中的视频）。这样，OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息，而非所有内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;大致流程 (以 github 为例)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;应用向 github 请求授权
github 为用户显示一个授权页面，用户在此页面确认是否同意应用的请求
如果用户同意授权，应用会获取到一个访问令牌 (access_token)，
通过此令牌，应用可以访问授权用户的数据&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.github.com/v3/oauth/" rel="nofollow" target="_blank" title=""&gt;详细流程 github 文档&lt;/a&gt;
&lt;img src="https://l.ruby-china.com/photo/2017/dc46560d8aaa21b7a28b8d63baffc694.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;转发到授权页面的 api&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET &lt;a href="https://github.com/login/oauth/authorize" rel="nofollow" target="_blank"&gt;https://github.com/login/oauth/authorize&lt;/a&gt;
    client_id  必须参数。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果用户同意授权，github 就会转发到第三方网站，同时提供一个 code 参数。&lt;br&gt;
第三方站点接着调用 api&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;POST &lt;a href="https://github.com/login/oauth/access_token" rel="nofollow" target="_blank"&gt;https://github.com/login/oauth/access_token&lt;/a&gt;
    client_id 必须
    client_secret 必须
    code 必须
获得 token，例如&lt;br&gt;
access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&amp;amp;scope=user%2Cgist&amp;amp;token_type=bearer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;访问资源&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET &lt;a href="https://api.github.com/user?access_token=" rel="nofollow" target="_blank"&gt;https://api.github.com/user?access_token=&lt;/a&gt;...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview" rel="nofollow" target="_blank" title=""&gt;devise 集成文档&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Thu, 23 Mar 2017 21:15:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/32625</link>
      <guid>https://ruby-china.org/topics/32625</guid>
    </item>
    <item>
      <title>Active Support 的 Concern 模块来由探究</title>
      <description>&lt;p&gt;ActiveSupport::Concern 模块作用：让 rails 类包含模块之后同时获得实例方法和类方法。
例如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'active_support'&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyConcernA&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;an_instance_method&lt;/span&gt; 
    &lt;span class="s2"&gt;"an_instance_method "&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_class_method&lt;/span&gt;
      &lt;span class="s2"&gt;"a_class_method"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MyConcernA&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a_class_method&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"a_class_method"&lt;/span&gt; 
&lt;span class="no"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;an_instance_method&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"an_instance_method "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么这个是如何实现的呢？
让我们从头说起。
两个基本的概念：include 和 extend&lt;br&gt;
include 让类获得实例方法&lt;br&gt;
extend 让类获得类方法&lt;br&gt;
最早使类同时获得实例方法和类方法的解决方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;A&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ClassMethods&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_class_method&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"a_class_method"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_instance_method&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"a_instance_method"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;W&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;W&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a_class_method&lt;/span&gt;
&lt;span class="n"&gt;a_class_method&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模块 A 覆写了钩子方法 included，当在类 W 中 include A 的时候会调用覆写后的方法。&lt;br&gt;
此时会把 W 作为一个参数传递进去，就是 included 钩子方法中的 base，所以就相当于 W 扩展了模块 ClassMethods，因此模块 ClassMethods 中的方法也就成为了类 A 的类方法。&lt;/p&gt;

&lt;p&gt;但是这个技巧本身存在一些问题。每个需要定义类方法的模块都需要定义一个相似的 included 钩子方法。
除此之外还有一个更为关键的问题，例如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;B&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="c1"&gt;#base.send :include, A&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;b_class_method&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"b_class_method"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;b_instance_method&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"b_instance_method"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b_class_method&lt;/span&gt;
&lt;span class="n"&gt;b_class_method&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; 
&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a_class_method&lt;/span&gt;
&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`a_class_method' for C:Class
B.a_class_method
a_class_method
 =&amp;gt; nil 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本来 a_class_method 应该是类 C 的类方法才对，可是这里出了问题。原因在哪里呢？&lt;br&gt;
在类C中 include B 此时参数 base 的值是 C&lt;br&gt;
而在模块 B 中 include A 时候参数 base 的值是 B，所以 a_class_method 成为模块 B 的一个类方法。&lt;/p&gt;

&lt;p&gt;为了解决这个问题，需要在模块 B 的钩子方法 included 中加入注释掉的代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="ss"&gt;:include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让类 C 调用一下模块 A 的钩子方法方法 included.
这样问题解决了，但是会造成其他的问题，如各个模块都包含了相似的代码，当超过一层模块包含的时候还有可能失败。&lt;/p&gt;

&lt;p&gt;为了解决上述问题，ActiveSupport::Concern 出现了。其封装了包含并且扩展的技巧，同时解决了链式包含的问题。&lt;br&gt;
一个模块可以通过扩展 concern 模块来定义自己的 ClassMethods 模块来实现包含并且扩展的功能，也就是文章最开始的例子。&lt;br&gt;
那么这个是如何实现的呢？看源码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Concern&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MultipleIncludedBlocks&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="s2"&gt;"Cannot define multiple 'included' blocks for a Concern"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
      &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@_dependencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;append_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@_dependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@_dependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="vi"&gt;@_dependencies.each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;dep&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;const_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ClassMethods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;const_defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ClassMethods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="vi"&gt;@_included_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;instance_variable_defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@_included_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;
&lt;span class="err"&gt;　＃&lt;/span&gt; &lt;span class="err"&gt;。。。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要是两个方法 extend 和 append_features&lt;br&gt;
当模块扩展 Concern 时候，会调用钩子方法 extended。在这个方法中为扩展它的类定义了一个&lt;a href="/_dependencies." class="user-mention" title="@_dependencies."&gt;&lt;i&gt;@&lt;/i&gt;_dependencies.&lt;/a&gt;默认值是 [].&lt;br&gt;
方法append_features是一个内核方法。和module#included类似。&lt;br&gt;
相同点：当包含一个模块的时候被调用。&lt;br&gt;
不同的：included 默认是空的，只有覆写之后才会有内容。而 append_features 则包含有实际的动作。用于检查被包含模块是否已经在包含类的祖先链上，如果不在则将该模块加入其祖先链。&lt;/p&gt;

&lt;p&gt;当一个模块扩展 ActiveSupport::Concern，我们称其为一个 concern&lt;br&gt;
接着我们来分析 append_features 的源码：&lt;br&gt;
主要分为两种情况：&lt;br&gt;
第一种情况&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyConcernB&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;an_instance_methodb&lt;/span&gt;
    &lt;span class="s2"&gt;"an_instance_method "&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_class_methodb&lt;/span&gt;
      &lt;span class="s2"&gt;"a_class_method"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_check&lt;/span&gt;
    &lt;span class="nb"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@_dependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MyConcernA&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码会先判断 base 是否是一个 concern.&lt;br&gt;
当前是一个 concern(MyConcernB) 在包含一个 concern(MyConcernA).此时包含的时候并没有真正的执行包含的动作，只是把链接放到一个依赖图中。也就是将 MyConcernA 放入数组&lt;a href="/_dependencies." class="user-mention" title="@_dependencies."&gt;&lt;i&gt;@&lt;/i&gt;_dependencies.&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;MyConcernB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_check&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;MyConcernA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="no"&gt;MyConcernB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a_class_method&lt;/span&gt;
&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`a_class_method' for MyConcernB:Module
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二种情况&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MyConcernB&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时就是 else 后边的情况了。&lt;br&gt;
首先会判断 MyClass 是否继承了 MyConcernB，也就是看 MyConcernB 是否在 MyClass 的继承链中。&lt;br&gt;
如果没有就进行最关键的步骤：concern(MyConcernB) 中的依赖（也就是&lt;a href="/_dependencies" class="user-mention" title="@_dependencies"&gt;&lt;i&gt;@&lt;/i&gt;_dependencies&lt;/a&gt;,上文已经打印出了值）会被递归包含到类 Myclass 中。这种最小化的依赖管理方式解决了之前链式包含的问题。&lt;br&gt;
此处的&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@_dependencies.each { |dep| base.send(:include, dep) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就相当于在 MyClass 中&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;include MyConcernA
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把所有的依赖的 concern(也就是&lt;a href="/_dependencies" class="user-mention" title="@_dependencies"&gt;&lt;i&gt;@&lt;/i&gt;_dependencies&lt;/a&gt;中的值) 都加入类的祖先链之后，&lt;br&gt;
需要把包含类自己（此处相当于 MyConcernB）也加入类的继承链中。这个通过调用 super 来实现。&lt;br&gt;
最后是要通过 ClassMethods 模块来扩展类，就像最初做的事情一样。&lt;/p&gt;

&lt;p&gt;以上内容全部来源于 ruby 元编程第十章。&lt;br&gt;
第一次写，还请大家多多指导。&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Mon, 20 Mar 2017 21:54:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/32585</link>
      <guid>https://ruby-china.org/topics/32585</guid>
    </item>
    <item>
      <title>单件类的疑问</title>
      <description>&lt;p&gt;最近在看 letter_avatar 这个 gem，看到如下代码有些疑问．&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/3897c916fae30d12bbdbbc229c7051d3.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在
class &amp;lt;&amp;lt; self
end
中定义方法，这个明白是给类 LetterAvatar::Avatar 定义类方法．&lt;/p&gt;

&lt;p&gt;可是在里边定义个类 Identity，是什么意思呢？&lt;/p&gt;

&lt;p&gt;Identity 和 LetterAvatar::Avatar 又有什么关系呢？&lt;/p&gt;

&lt;p&gt;是因为 Identity 的方法只需要在 LetterAvatar::Avatar 类中使用才这样做的吗？&lt;/p&gt;

&lt;p&gt;谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Wed, 08 Mar 2017 16:55:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/32483</link>
      <guid>https://ruby-china.org/topics/32483</guid>
    </item>
    <item>
      <title>数组分页,除了 Kaminari.paginate_array 有更好的方法吗?</title>
      <description>&lt;p&gt;最近做了个统计的表格，表格中的很多元素都是通过计算出来的，因此不能直接分页&lt;/p&gt;

&lt;p&gt;目前使用了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;paginatable_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Kaminari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate_array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_array_object&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;per&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;方法，但是每次都要把整个结果集计算出来&lt;/p&gt;

&lt;p&gt;而不是查看第一页的时候只取第一页的数据&lt;/p&gt;

&lt;p&gt;有别的好的方法吗？谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Fri, 24 Feb 2017 18:19:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/32375</link>
      <guid>https://ruby-china.org/topics/32375</guid>
    </item>
    <item>
      <title>网站升级到 https 后 Devise 登录请求会从 https 变成 http,其他页面正常</title>
      <description>&lt;p&gt;图 1：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/7c42ac3ef07b36047d55ad14f35c7cb5.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;图 2：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/7e8f3c37f78dcef8e00497248aba78c4.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如图所示：
sing_in    这个请求是 https&lt;/p&gt;

&lt;p&gt;但是到登录成功之后的 root 请求却变成了 http&lt;/p&gt;

&lt;p&gt;查看 log 发现有如下信息：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;I&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="no"&gt;T15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;59.873096&lt;/span&gt; &lt;span class="c1"&gt;#6843]  INFO -- : Redirected to http://bset.yesqin.com/user/root&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此应该是重定向的时候出问题了。&lt;/p&gt;

&lt;p&gt;打印出重定向时候的请求信息：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;I&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="no"&gt;T15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;448955&lt;/span&gt; &lt;span class="c1"&gt;#11578]  INFO -- :     after_sign_in_path_for(resource):  /user/root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按理说登录后的请求也应该是 https 才对啊，可为啥变了呢？&lt;/p&gt;

&lt;p&gt;谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Fri, 30 Dec 2016 15:42:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/32025</link>
      <guid>https://ruby-china.org/topics/32025</guid>
    </item>
    <item>
      <title>貌似发现了个论坛搜索的 Bug</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/e26f1b0352e24484928e31a051b15dc0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;依次点击了
rspec 测试求时间方法&lt;/p&gt;

&lt;p&gt;自然语言时间解析&lt;/p&gt;

&lt;p&gt;下班时间都怎么度过的&lt;/p&gt;

&lt;p&gt;然后打开的页面如图所示分别是&lt;/p&gt;

&lt;p&gt;自然语言时间解析&lt;/p&gt;

&lt;p&gt;下班时间都怎么度过的&lt;/p&gt;

&lt;p&gt;Newrelic 监控的 respons 时间和实际测试时间差别很大&lt;/p&gt;

&lt;p&gt;&lt;a href="/huacnlee" class="user-mention" title="@huacnlee"&gt;&lt;i&gt;@&lt;/i&gt;huacnlee&lt;/a&gt; &lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Tue, 08 Nov 2016 09:34:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/31545</link>
      <guid>https://ruby-china.org/topics/31545</guid>
    </item>
    <item>
      <title>transaction 中 例如 create! 又包含在一个小的 begin..commit 中何解?</title>
      <description>&lt;p&gt;本来是希望能减少提交次数，可是为了保证能正确回滚，又要使用 create! 这种方法，然这个又包含在一个单独的 transaction 中，有没有比较好的方法来解决这个问题。谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Wed, 31 Aug 2016 13:33:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/30947</link>
      <guid>https://ruby-china.org/topics/30947</guid>
    </item>
    <item>
      <title>目前使用 paranoia 软删除,看有人喜欢给所有的表都加入软删除,有这个必要吗?</title>
      <description>&lt;p&gt;如题&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Mon, 29 Aug 2016 20:51:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/30927</link>
      <guid>https://ruby-china.org/topics/30927</guid>
    </item>
    <item>
      <title>［已解决］字符串转换成 hash </title>
      <description>&lt;p&gt;w = "{'response':{'id':'19999','period':'5','fieldList':[{'date':'2016-06-23','time':'00:01:07'},{'date':'2016-06-23','time':'00:06:07'}]}}"&lt;/p&gt;

&lt;p&gt;uri = URI(price_api)
price_data = Net::HTTP.get(uri)
使用如上方法获取 api 的结果是一个字符串
如上字符串如何才能转换成 hash?&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Thu, 23 Jun 2016 20:57:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/30353</link>
      <guid>https://ruby-china.org/topics/30353</guid>
    </item>
    <item>
      <title>rake assets:precompile RAILS_ENV=production 没有生成*.gz 文件</title>
      <description>&lt;p&gt;最近在看 rake assets:precompile 配合 nginx gzip 使静态文件进行压缩传输
运行这个命令之后却没有生成*.gz 文件，本以为自己改到了 asset 相关的配置，查看之后无果
在网上看了下，虽然可以写个 rake 任务自己进行压缩，同时 nginx 也提供这个功能，
但是还是想知道自己项目的问题出在哪里了？求指点。&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Tue, 07 Jun 2016 16:48:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/30234</link>
      <guid>https://ruby-china.org/topics/30234</guid>
    </item>
    <item>
      <title>看到几篇文章，从环境搭载到部署 (Nginx + Unicorn)，每一步都讲解的很清楚，着实不错。</title>
      <description>&lt;p&gt;第一篇：&lt;a href="http://vladigleba.com/blog/2014/03/05/deploying-rails-apps-part-1-securing-the-server/" rel="nofollow" target="_blank"&gt;http://vladigleba.com/blog/2014/03/05/deploying-rails-apps-part-1-securing-the-server/&lt;/a&gt;
本篇讲了服务器选择，登陆，部署账户创建权限设置。
第二篇：&lt;a href="http://vladigleba.com/blog/2014/03/14/deploying-rails-apps-part-2-setting-up-the-server/" rel="nofollow" target="_blank"&gt;http://vladigleba.com/blog/2014/03/14/deploying-rails-apps-part-2-setting-up-the-server/&lt;/a&gt;
本篇讲了服务器基本环境的搭建，nodejs,nginx,ruby 等。
第三篇：&lt;a href="http://vladigleba.com/blog/2014/03/21/deploying-rails-apps-part-3-configuring-unicorn/" rel="nofollow" target="_blank"&gt;http://vladigleba.com/blog/2014/03/21/deploying-rails-apps-part-3-configuring-unicorn/&lt;/a&gt;
本篇比较了 passenger,unicorn,puma 之间的优缺点，和 unicorn 的安装配置。
第四篇：&lt;a href="http://vladigleba.com/blog/2014/03/27/deploying-rails-apps-part-4-configuring-nginx/" rel="nofollow" target="_blank"&gt;http://vladigleba.com/blog/2014/03/27/deploying-rails-apps-part-4-configuring-nginx/&lt;/a&gt;
本篇讲解了 nginx 的配置，阐述了相关 directive 的作用。&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Thu, 26 May 2016 11:01:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/30130</link>
      <guid>https://ruby-china.org/topics/30130</guid>
    </item>
    <item>
      <title>Can't verify CSRF token authenticity 看了社区之前同样问题依然没有解决.</title>
      <description>&lt;p&gt;登陆使用的是 devise 做的
然后在前台使用$.ajax 发送 post 请求，出现了这个警告.
尝试了如下方法：&lt;/p&gt;

&lt;p&gt;1.在 application.js 中添加如下代码&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajaxSetup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;beforeSend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-CSRF-Token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta[name="csrf-token"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.试着将$('meta[name="csrf-token"]').attr('content') 的值作为一个 ajax 的一个参数直接传过去.
都没有解决。&lt;/p&gt;

&lt;p&gt;用 firefox 查看 request headers 里边也有 X-CSRF-Token
然后看有的朋友说可能是传入的和服务器端的 token 不一样。那该咋看服务器端的 token 值呢.
这个到底是哪里的问题呢？
谢谢了。&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Sun, 10 Jan 2016 15:38:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/28676</link>
      <guid>https://ruby-china.org/topics/28676</guid>
    </item>
    <item>
      <title> 已解决 CentOS iptables 配置完成之后,端口依然无法连接.</title>
      <description>&lt;p&gt;先说下系统是 centos7 ,firewall 已经关闭了。现在使用 iptables 来管理.
iptables --line-numbers -n -L 命令结果截图如下:
&lt;img src="https://l.ruby-china.com/photo/2015/dff9a3f92d59e1ca1d8bdfa4b2a30d66.jpg" title="" alt=""&gt;
然而在本地使用 telnet ip 8000 依然无法连接
使用命令 netstat -nlpt 结果如下:
&lt;img src="https://l.ruby-china.com/photo/2015/b4e61f258a4623b1daf493625a17a4bc.jpg" title="" alt=""&gt;
22,80,3306 端口都是正常的.
谢谢。&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Wed, 30 Dec 2015 11:09:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/28566</link>
      <guid>https://ruby-china.org/topics/28566</guid>
    </item>
    <item>
      <title>gem  'net-ssh'　调用 Net::SSH.start ('host', 'user', :password =&gt; "password") 方法时候 用户名或者密码错误情况下程序卡死</title>
      <description>&lt;p&gt;如下：这是我的代码：
&lt;img src="https://l.ruby-china.com/photo/2015/5cfd34468978032efe841430e30eb5e1.jpg" title="" alt=""&gt;
实际发现如果用户名或者密码错误的时候，程序会卡死．
看了一下源码如下：
&lt;img src="https://l.ruby-china.com/photo/2015/5b071017e1fb3b9d5be3d7c0a1bd4297.jpg" title="" alt=""&gt;
按理用户名密码不对应该抛出异常才对啊．
然后在 rails c 里边试了一下，发现如果用户名密码不对，会让你继续输入密码，如果三次全部错误才会报错．
那么这该如何解决呢？
谢谢！&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Sun, 13 Sep 2015 12:12:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/27308</link>
      <guid>https://ruby-china.org/topics/27308</guid>
    </item>
    <item>
      <title>API 编写显示问题</title>
      <description>&lt;p&gt;本人现在用 grape,jbuilder 编写 api，现在遇到了一个问题.
如下:
现在有两个数据集合 a 和 b，前台显示是要 4 个 a 的元素，1 个 b 的元素这样的.a 和 b 元素的字段也都不相同.
也就是说返回的 json 数据应该是四个 a 中元素，一个 b 中元素这样的.
求解.....&lt;/p&gt;</description>
      <author>bajiudongfeng</author>
      <pubDate>Wed, 28 Jan 2015 15:40:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/23990</link>
      <guid>https://ruby-china.org/topics/23990</guid>
    </item>
  </channel>
</rss>
