<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>poiyzy (Roy)</title>
    <link>https://ruby-china.org/poiyzy</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>新博文：介绍 Fengche.co 中对 Observer 的使用</title>
      <description>&lt;p&gt;新博客，介绍 Fengche.co 中为什么使用 Observer，希望和大家一起交流 :)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://yangzhuoyu.com/why-we-use-rails-observer/" rel="nofollow" target="_blank"&gt;http://yangzhuoyu.com/why-we-use-rails-observer/&lt;/a&gt;&lt;/p&gt;</description>
      <author>poiyzy</author>
      <pubDate>Thu, 20 Mar 2014 16:51:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/18044</link>
      <guid>https://ruby-china.org/topics/18044</guid>
    </item>
    <item>
      <title>新的起点，加入远程创业团队 Pragmatic.ly</title>
      <description>&lt;p&gt;加入 Pragmatic.ly 一个月了，写了一篇新博文，和大家分享一下在一个国内优秀远程创业团队中所学到的东西。&lt;/p&gt;

&lt;p&gt;对于远程工作甚至是创业，在我加入这个团队之前，一直都很好奇。体验了一个月，我来分享一下在这个团队中大家是如何有效利用自由时间享受工作和生活。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://yangzhuoyu.com/first-month-in-pragmatic-ly/" rel="nofollow" target="_blank"&gt;http://yangzhuoyu.com/first-month-in-pragmatic-ly/&lt;/a&gt;&lt;/p&gt;</description>
      <author>poiyzy</author>
      <pubDate>Wed, 16 Oct 2013 13:43:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/14791</link>
      <guid>https://ruby-china.org/topics/14791</guid>
    </item>
    <item>
      <title>使用 Mailgun 建立一个数据驱动方法来保持用户 Email 更新</title>
      <description>&lt;p&gt;在 &lt;a href="/knwang" class="user-mention" title="@knwang"&gt;&lt;i&gt;@&lt;/i&gt;knwang&lt;/a&gt; 老师的指导下，最近给 Mailgun 写了一篇博客，原文在这里 - &lt;a href="http://blog.mailgun.net/post/45366504974/building-a-data-driven-approach-to-keeping-users" rel="nofollow" target="_blank"&gt;http://blog.mailgun.net/post/45366504974/building-a-data-driven-approach-to-keeping-users&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;无耻小请求：小菜鸟第一次写这样的技术博文，居然上了 Hacker News, 各位大大们帮忙在 HN 上投个票吧。&lt;/p&gt;
&lt;h2 id="Building a data-driven approach to keeping user contacts up-to-date"&gt;Building a data-driven approach to keeping user contacts up-to-date&lt;/h2&gt;
&lt;p&gt;This blog was written by &lt;a href="http://twitter.com/poiyzy" rel="nofollow" target="_blank" title=""&gt;Roy Young&lt;/a&gt;. Roy is a student from &lt;a href="http://www.gotealeaf.com" rel="nofollow" target="_blank" title=""&gt;Tealeaf Academy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you remember every email address you use with every one of your online accounts? You know, the accounts you use with your spam email address, versus your "real" email, versus your work email?  Probably not. And neither do your users. That is why it makes sense to periodically confirm that you have the best contact information on file for your customers.  You could do this every month, or once a year, or take a data driven approach that lets you focus only on the customers that will most likely find your notices to update their contact information helpful, rather than annoying.&lt;/p&gt;
&lt;h4 id="Application logic for prompting user to update their email address"&gt;Application logic for prompting user to update their email address&lt;/h4&gt;
&lt;p&gt;When you send email to an email address that no longer exists, the receiving party (e.g. Gmail or Yahoo) will bounce the email back to you.  If you listen to these bounces, your app can react appropriately, creating a great user experience while keeping your customer database up-to-date.
This blog outlines a sample application built on Heroku that uses Mailgun to listen to these bounced emails so that users with bad email addresses can continue to receive monthly invoice emails.  The same logic could be applied to any of your emails, however, making sure that your users stay engaged and up-to-date with your company via email.&lt;/p&gt;

&lt;p&gt;This post is built around the following workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Some user signs up for the app with some email address.&lt;/li&gt;
&lt;li&gt;App sends out automatic monthly billing emails to all users using Heroku Scheduler.&lt;/li&gt;
&lt;li&gt;User changes job, leaving their email address in system no longer valid.&lt;/li&gt;
&lt;li&gt;Emails sent to the old address bounce, and Mailgun sends a POST via the bounce webhook to the app.&lt;/li&gt;
&lt;li&gt;The app marks the user with a special status and stops sending the user monthly billing emails.&lt;/li&gt;
&lt;li&gt;When the user signs in to your app the next time, a flash message displays about email no longer being valid and takes them to the account page and prompts them to update their email.&lt;/li&gt;
&lt;li&gt;User updates their email address and they continue to receive the monthly billing emails.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've created a &lt;a href="http://mailgun-demo.heroku.com" rel="nofollow" target="_blank" title=""&gt;demo site&lt;/a&gt; for this article and made the source code available on &lt;a href="https://github.com/poiyzy/mailgun_example" rel="nofollow" target="_blank" title=""&gt;Github&lt;/a&gt;. This demo walks a novice through the entire process of setting up this functionality. Click &lt;a href="#receiving-notification-of-bounced-emails-from-mailgun" title=""&gt;here&lt;/a&gt; if you want to skip directly to integrating bounce web hooks into your a app.&lt;/p&gt;
&lt;h4 id="Sending monthly invoice emails via Mailgun using Heroku Scheduler"&gt;Sending monthly invoice emails via Mailgun using Heroku Scheduler&lt;/h4&gt;
&lt;p&gt;To send monthly invoices to your users, you'll need to write code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy customized invoice emails to your hundreds, or thousands or millions of customers.&lt;/li&gt;
&lt;li&gt;Actually Schedule the deployment of these emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at each of these in turn.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Deploying customized invoce emails using Mailgun&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sending emails via Mailgun simply requires an &lt;a href="http://www.mailgun.com/pricing" rel="nofollow" target="_blank" title=""&gt;account&lt;/a&gt;.  For the purposes of this tutorial, I'll you've already done that. From there, you'll want to send your customer list to Mailgun, along with any variables that you want dynamically inserted into the emails as a JSON file and the HTML or text body of the email.  Here is the code.&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;class MailgunGateway
  include Rails.application.routes.url_helpers

  def send_batch_message(users)
    RestClient.post(messaging_api_end_point,
    from: "Mailgun Demo &lt;span class="nt"&gt;&amp;lt;billing&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="err"&gt;["&lt;/span&gt;&lt;span class="na"&gt;mailgun_domain_name&lt;/span&gt;&lt;span class="err"&gt;"]}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;",
    to: users.map(&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;:email).join(", "),
    subject: "Monthly Billing Info",
    html: billing_info_text,
    :"h:Reply-To" =&amp;gt;  "billing@#{ENV["mailgun_domain_name"]}",
    :"recipient-variables" =&amp;gt; recipient_variables(billing_recipients)
    )
  end

  private

  def api_key
    @api_key ||= ENV["mailgun_api_key"]
  end

  def messaging_api_end_point
    @messaging_api_end_piont ||= "https://api:#{api_key}@api.mailgun.net/v2/#{ENV["mailgun_domain_name"]}/messages"
  end

  def billing_recipients
    @users ||= User.where(locked: false)
  end

  def recipient_variables(recipients)
    vars = recipients.map do |recipient|
      "\"#{recipient.email}\": {\"name\":\"#{recipient.fullname}\"}"
    end
    "{#{vars.join(', ')}}"
  end

  def billing_info_text
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;-EMAIL&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;

Hi %recipient.name%,

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
Your bill for the current month is now available, please click on
&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
#{billing_url}
&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
to see details.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Reply to this email directly&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
EMAIL
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;send_batch_message&lt;/code&gt; method, we also use Recipient Variables so that we can define variables to customize each email.  You'll notice that we are inserting the user's first name in the email.  You could compose an invoice email that includes details like account number, invoice account, or anything else you like, but I'm keeping things simple.&lt;/p&gt;
&lt;h4 id="Sending out automatic monthly billing emails with Heroku Scheduler"&gt;Sending out automatic monthly billing emails with Heroku Scheduler&lt;/h4&gt;
&lt;p&gt;Once we have our method for creating and deploying emails via Mailgun, we need to write some code that automatically triggers this method.  I've chosen to deploy this app on Heroku and am using Heroku Scheduler because it does the job, is a free add-on, and is easy to integrate.&lt;/p&gt;

&lt;p&gt;Heroku Scheduler will run a specified Rake task at a specified time. So here we need to build a Rake task to send billing emails using Mailgun in &lt;code&gt;lib/tasks/scheduer.rake&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;desc "This task is called by the Heroku scheduler add-on"

task :send_billing_info =&amp;gt; :environment do
  if DateTime.now.mday == 1
    User.where(locked: false).find_in_batches(batch_size: 1000) do |group|
      MailgunGateway.new.send_batch_message(group)
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Beacuse Heroku Scheduler "FREQUENCY" setting has only three selectors-- "daily, hourly, every 10 mins"-- we need to check whether the current day is the first day of the month using &lt;code&gt;if DateTime.now.mday == 1&lt;/code&gt;.  If it is, the email is deployed. If not, no email.&lt;/p&gt;

&lt;p&gt;When we send billing invoices to users, we need to find the email addresses of the customer we need to invoice.&lt;/p&gt;

&lt;p&gt;In this sample app's very simple database shcema, we have a &lt;code&gt;:locked&lt;/code&gt; attribute that is set to True when an email has bounced previously (more on that below). So our query simply pulls all our users where this field is False.&lt;/p&gt;

&lt;p&gt;To actually send the email, I'm using Maigun &lt;a href="http://documentation.mailgun.net/user_manual.html#batch-sending" rel="nofollow" target="_blank" title=""&gt;Batch Sending&lt;/a&gt;. Beacuse the maximum number of recipients allowed per API call is 1,000, we use &lt;code&gt;find_in_batches&lt;/code&gt; to send billing emails to Mailgun in batches of 1000 recipients.&lt;/p&gt;

&lt;p&gt;This is what the Scheduler setting look like in Heroku:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://gist.github.com/poiyzy/5045565/raw/983ba476c562a412dcf8b9a224e4616e1f628960/heroku.png" title="" alt="Heroku"&gt;&lt;/p&gt;
&lt;h4 id="Receiving notification of bounced emails from Mailgun"&gt;
&lt;a id="webhook"&gt;&lt;/a&gt;Receiving notification of bounced emails from Mailgun&lt;/h4&gt;
&lt;p&gt;When an email is bounced, Mailgun will send a POST via the bounce webhook to our app. You need to set the callback url under "Bounces" in the Mailgun Control Panel. In this demo, my callback url is "&lt;a href="http://mailgun-demo.herokuapp.com/api/bounced_mails" rel="nofollow" target="_blank" title=""&gt;http://mailgun-demo.herokuapp.com/api/bounced_mails&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;&lt;img src="https://gist.github.com/poiyzy/5045565/raw/096e3d52745d307b17d22fb927360a1e82a11cad/callback_url.png" title="" alt="callback_url"&gt;&lt;/p&gt;

&lt;p&gt;When the app receives the notification from Mailgun, the app will find the email address in the customer database, and set the user status to locked, so the app will not send billing email to the user until the user update his email address.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Api::BouncedMailsController &amp;lt; ApplicationController
  skip_before_filter :verify_authenticity_token

  def create
    if verify(ENV["mailgun_api_key"], params[:token], params[:timestamp], params[:signature])
      user = User.find_by_email(params[:recipient])
      if user &amp;amp;&amp;amp; params[:event] == "bounced"
        user.lock!
      end
      head(200)
    end
  end

  private
  def verify(api_key, token, timestamp, signature)
    signature == OpenSSL::HMAC.hexdigest(
      OpenSSL::Digest::Digest.new('sha256'),
      api_key,
      '%s%s' % [timestamp, token])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;user.lock!&lt;/code&gt; is to set user status to locked, so our Rake Task will never send emails to this user. This method is set in User model.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base

  …

  def lock!
    self.locked = true
    save(validate: false)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;verity&lt;/code&gt; method is to verify the webhook is originating from Mailgun, otherwise any others could send a fake POST to lock the users in your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details on configuring web hooks you can read the Mailgun documentation - &lt;a href="http://documentation.mailgun.net/user_manual.html#manual-webhooks" rel="nofollow" target="_blank" title=""&gt;Events/Webhooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the end, we return a 200 header to tell Mailgun that this interaction is successful, otherwise Mailgun will think our server is down and will keep trying to call our webhook.&lt;/p&gt;
&lt;h4 id="Displaying the flash screen for a user to update their email"&gt;Displaying the flash screen for a user to update their email&lt;/h4&gt;
&lt;p&gt;Once the app knows which users have a bad email address, we need to display a flash screen to the user upon their next login with a prompt to verify their contact information on file.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SessionsController &amp;lt; ApplicationController
  def new
    redirect_to home_path if current_user
  end

  def create
    user = User.find_by_email(params[:email])
    if user &amp;amp;&amp;amp; user.authenticate(params[:password])
      session[:user_id] = user.id
      if user.locked
        flash[:error] = "Your Email Address is invalid, please update it."
        redirect_to edit_user_path(current_user)
      else
        redirect_to home_path
      end
    else
      flash[:error] = "Incorrect email or password. Please try again."
      redirect_to sign_in_path
    end
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_path
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://gist.github.com/poiyzy/5045565/raw/d54b23ae8032a9adfa0b4f18180feab82260b2de/update_password.png" title="" alt="update_email"&gt;&lt;/p&gt;

&lt;p&gt;Once they've updated their email address, we set the locked attribute back to false so that they will continue to receive emails. &amp;nbsp;&lt;/p&gt;

&lt;p&gt;That's it. Now, when your users email's bounce, you can prompt them to update their information, providing a great user experience with the added benefit of keeping your customer data fresh.&lt;/p&gt;</description>
      <author>poiyzy</author>
      <pubDate>Sat, 23 Mar 2013 01:34:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/9689</link>
      <guid>https://ruby-china.org/topics/9689</guid>
    </item>
    <item>
      <title>我的成长 (新手)</title>
      <description>&lt;p&gt;我接触 Rails 大概有 1 年半的时间，目前还是新手一名。通过最近半年的专心学习，感触很多，所以分享出来，以便帮助和我情况相同的新手。&lt;/p&gt;

&lt;p&gt;我在接触 Ruby 之前做了两年的 PHP，后来很偶然的一个机会开始学习 Ruby。自学 Ruby 的时候无非就是看书，泡论坛，自己做玩具，在 Github 上看别人的代码，订阅 Screencast。大概半年的时间，感觉是入门了，可是还是无法深入。总是停留在做玩具的阶段，后来帮朋友用 Ruby 重构一个 PHP 的网站算是积累了一些解决实际问题的经验，不过这些经验还是少的可怜。&lt;/p&gt;

&lt;p&gt;我自学的时候最大的问题是对自己不自信（因为首先是新手，其次我本科不是计算机专业的），所以就是混论坛也不敢说话、不敢分享，不是怕被别人笑话，而是怕误导了其他新手，但是正像 &lt;a href="/lgn21st" class="user-mention" title="@lgn21st"&gt;&lt;i&gt;@&lt;/i&gt;lgn21st&lt;/a&gt; 在 Teahour 当中提到的正是不自信才驱动自己不断的去学习去提高。可是如何去提高自己呢？&lt;/p&gt;

&lt;p&gt;就这样一年过去了，那个时候我感觉和这里的很多朋友一样，非常渴望找一个师傅带带自己（因为自己周围没有做 Ruby 的人可以请教），最近在参加 &lt;a href="/knwang" class="user-mention" title="@knwang"&gt;&lt;i&gt;@&lt;/i&gt;knwang&lt;/a&gt; 的 &lt;a href="http://www.gotealeaf.com" rel="nofollow" target="_blank" title=""&gt;Gotealeaf&lt;/a&gt;  Intermediate 课程，课程进行过半来分享一下有了老师以后的感觉。&lt;/p&gt;

&lt;p&gt;课程本身的内容就不说了，很赞。自学的时候总是对自己定位不清楚，不知道自己欠缺什么，终于知道自己欠缺什么的时候，但是不知道该看什么，走了很多弯路。很多时候，看一本书，看到一半的时候发现这本书大部分东西都很初级，提升并不是很多，但是看一半了又不想放下，这样看完一本书提高并不多。再者就是太难了，耐着性子看好几天最后放弃了，这样一来二去的花费不少时间。&lt;/p&gt;

&lt;p&gt;在这次课程上，我最激动的是终于完成了找到一个师傅的心愿！在课程中，Kevin 哥给每个学员做了 1 对 1 的谈话，关于你的目标是什么？Where you are？并且给予了每个人单独的建议。一下子我就明朗了，知道具体该做，订阅什么 Screencasts，读什么书…… He gets me on the fast track!&lt;/p&gt;

&lt;p&gt;说了这么多废话，给大家分享一些很棒的付费内容（大家多次推荐的就不再推荐了）:&lt;/p&gt;

&lt;p&gt;Course:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;当然是 &lt;a href="http://www.gotealeaf.com" rel="nofollow" target="_blank" title=""&gt;Gotealeaf&lt;/a&gt; 啦！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Books:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="/Xdite" class="user-mention" title="@Xdite"&gt;&lt;i&gt;@&lt;/i&gt;Xdite&lt;/a&gt; 的 &lt;a href="http://rails-101.logdown.com/" rel="nofollow" target="_blank" title=""&gt;Rails 101&lt;/a&gt; 和 &lt;a href="http://rails-101.logdown.com/books/3-essential-rails-pattern" rel="nofollow" target="_blank" title=""&gt;Essential Rails Design Patterns&lt;/a&gt;（这本现在还没有完结）&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://everydayrails.com/" rel="nofollow" target="_blank" title=""&gt;Everyday Rails Testing with RSpec&lt;/a&gt; （测试入门）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Screencasts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.destroyallsoftware.com/screencasts" rel="nofollow" target="_blank" title=""&gt;Destroy All Sfotware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://devblog.avdi.org/rubytapas/" rel="nofollow" target="_blank" title=""&gt;Ruby Tapas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://practicingruby.com/" rel="nofollow" target="_blank" title=""&gt;Practice Ruby&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;都是收费内容。为自己投资，值得！&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A student from Gotealeaf, Thanks Kevin to bring me to Ruby world!&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>poiyzy</author>
      <pubDate>Wed, 27 Feb 2013 17:42:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/8985</link>
      <guid>https://ruby-china.org/topics/8985</guid>
    </item>
    <item>
      <title>在 Gemfile 中通过 bundle install 安装源自 github 的 gem 后如何删除 gem 呢？</title>
      <description>&lt;p&gt;难道直接 bundle show [Gem_Name] 找到 path 后删除文件夹么？&lt;/p&gt;</description>
      <author>poiyzy</author>
      <pubDate>Tue, 09 Oct 2012 19:20:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/5945</link>
      <guid>https://ruby-china.org/topics/5945</guid>
    </item>
    <item>
      <title>现在的收藏功能一直不能用啊</title>
      <description>&lt;p&gt;收藏了好多东西，现在有时间想去看看学习学习，可是一直打不开啊。  &lt;/p&gt;</description>
      <author>poiyzy</author>
      <pubDate>Mon, 02 Jul 2012 16:31:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/4083</link>
      <guid>https://ruby-china.org/topics/4083</guid>
    </item>
  </channel>
</rss>
