Photo - Qihuan Piao

Hi I'm Qihuan Piao, I'm a Rails developer and I like to make stuff.

Here I share articles and tips about web development, inpirations and thoughts I get from books and daily life.

I believe that English is a MUST skill for me to become a great engineer, so I’m trying to to write posts in it these days.

Use ActiveJob in Rails 4.1

ActiveJob is the headline feature in Rails 4.2, the Active Job Basics on RailsGuides explains the philosophy and usage very well, make sure you’ve checked that first. However there’re some gochas if you want to use it right now in your Rails 4.1 app. Here I’m gonna show you how to install it in 4.1, and things you need to take extra care of.

Install ActiveJob in Rails 4.1

Add activejob to your Gemfile then bundle install.

Create a active_job.rb file under config/initializers and paste code below.

1
2
3
require 'active_job'
# or any other supported backend such as :sidekiq or :delayed_job
ActiveJob::Base.queue_adapter = :inline

Now you should be abel to load ActiveJob in your rails app without error.

Note that the one you installed is not the one inside the rails repository, that has a version of 4.2.0.beta2 same as Rails at the time of writing, the one you installed is version 0. You can find the archived source code from its original repository.

Creating a Job

To create a job, you have to manually create the app/jobs folder first, then follow the same naming convention to create your job class file like app/jobs/guests_cleanup_job.rb.

1
2
3
4
5
6
7
class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

Enqueuing the Job

1
2
GuestsCleanupJob.enqueue(record)
GuestsCleanupJob.enqueue(record, options)

Differences between latest ActiveJob

  • no rails generator for jobs.
  • no callback mechanism like before_enqueue, before_perform etc.
  • doesn’t load itself to rails app by default, that’s why you need a initializer to load it manually.
  • enqueue syntax is slightly different, in Rails 4.2.beta2 enqueue has changed to perform_later.
  • internally it’s using activemodel-globalid instead of GlobalID(GlobalID is renamed from activemodel-globalid).
  • setting backend syntax is slightly different
1
2
3
4
5
# Rails 4.2.beta2
Rails.application.config.active_job.queue_adapter = :delayed_job

# Rails 4.1
ActiveJob::Base.queue_adapter = :delayed_job

p.s. I haven’t checked ActionMailer. I’m currently using it with DelayedJob and so far so good.

Summary

ActiveJob is very convenient, it provides a unified interface for the job infrastructure that allows you to switch the backend easily.

But as you can see there’re big diffs between the latest developed version and the one now we’re able to install in Rails 4.1.

Is it worth it to make the effort to try it now, and push these small upcoming changes when you upgrade to Rails 4.2 to your mental stack? My suggestion is if you’re just right about to implement a queue system and willing to adapt to it, then it’s OK, otherwise maybe better just leave the current app running as is and wait for a more mature timing.

RubyKaigi 2014 - 3 Good Talks for Rails Developers

I went to Rubykaigi 2014 last week, there were lots of great talks about ruby itself, rails tips and more. Here I’ll share 3 things I feel worth to share for rails developers.

Synvert

Speaker page on Rubykaigi2014

Synvert = syntax + convert, makes it easy to convert ruby code automatically

Synvert is a gem made by the same author of bullet gem. Using it you can convert your rails 3 before_filter to rails 4 syntax before_action, rspec should have to expect(…).to have or ruby 1.8 hash to 1.9 hash syntax with just one command, and it does support more. It’s well tested so no worry for human error.

You could define your own rules to convert code, or use those built-in very common used snippets for rails, rspec and factory_girl.

Installation and usage

1
2
3
4
5
6
gem install synvert

# fetch snippets to ~/.synvert directory
synvert --sync

synvert -r factory_girl/use_short_syntax

Run git diff I got

1
2
- user = FactoryGirl.create(:user)
+ user = create(:user)

How does it work

AST(Abstract Syntax Tree) is used internally, it parses your source code to meaningful structures. It’s like grammar in English, one phrase can be broken down to words and some are noun, some are verb, it’s like saying “let’s replace all the verb ‘walk’ to ‘run’”. I highly recommend you to check out the slide if you want to learn more.

Example of attributes for AST node

For instance, it breaks a method call to receiver, message and arguments. A typical method call is a type of “send node”, class definition is “class node” and there is “block node”.

Links

Going the distance

Speaker page on Rubykaigi2014

To be honest I’m not sure I fully understand the algorithm of calculating distance between 2 words, but the great part is @ schneems used it to improve rails generator command to suggest possible commands when you had a typo.

Please see the pull request for details. Emit suggested generator names when not found #15497

If you ever make a cli command, you could follow the same pattern, using the algorithm to suggest candidates instead of just showing plain error message.

Good to see how how he adapted scientific algorithm to solve real world problem.

Speeding up Rails 4.2

Speaker page on Rubykaigi2014

Interesting to see how @tenderlove find room for optimization and how to measure it.

Tools mentioned:

  • benchmark-ips, benchmarks a blocks iterations/second. For short snippits of code, ips automatically figures out how many times to run the code to get interesting data. No more guessing at random iteration counts!
  • allocation_tracer, allows to trace object allocation.

Slide on speakerdeck

Recover Your Local Octopress Repository

Here I’ll show you how to recover or restore your local octopress repository, for instance you’re on a new machine, or just lost your local copy of it.

It requries you to have a repository on github that you’ve deployed your octopress posts.

First I recommend that you read this post: Clone Your Octopress to Blog From Two Places, the Recreating a local Octopress repository section.

And here is what worked for me, please replace kinopyo with your user name.

1
2
3
4
5
6
7
8
git clone -b source git@github.com:kinopyo/kinopyo.github.com.git octopress
cd octopress

gem install bundler
bundle install

rake preview
open http://localhost:4000

Tips on Converting HTML to Markdown

Somehow the latest 3 posts are still missing after I git cloned my remote repository. I checked in advance and saved those copies as html. And after I restored my octopress repository I used Pandoc to convert those html to markdown.

It works like:

1
pandoc -f html -t markdown your_html_file.html

Thoughts on Disaster

p.s. I stored my blog octopress repository on Dropbox and somehow it just all gone, it really shocked me. Probably it was my fault not theirs, but still I always thought Dropbox is a safe place, but I don’t even have a clue why it happended and somehow could’t see the history and restore from their website. After I noticed it everytime I came up with something to write I’ve had used it as an excuse, or maybe I was not in the mood to deal with it(also I didn’t know how…).

So I need a fallback solution about Dropbox, every disaster changes you to rethink about current seems comfortable situation. And hope I’ll write more often :)

Remote Work, Day One: Trust

Lots of big changes happened in my life recently, one of them is my workplace. Lucky enough, now I got a chance to embrace the modern style of work, that is working remotely.

I suppose you know more or less about remote. 37signals has published their new book, REMOTE: Office Not Required, where all the benefits you can get from remote is described. I’m half way done of that book, and probably will write another post once I finished it and compared it to my real experience. So here I’m gonna talk about only one thing, a key thing, trust.

I’m a true believer of remote work. As in the book says,

The bottom line is that you shouldn’t hire people you don’t trust, or work for bosses who don’t trust you. If you’re not trusted to work remotely, why are you trusted to do anything at all?

I totally agree with that. But in reality is, well at least the environment around me is even every single manager or boss is good and kind, we still fall into the traditional working style, 9:00 - 18:00+ in the office, that’s the norm, so dominant, I didn’t even try to convince anyone to change that.

Fortunately I got a very unique opportunity to do remote work. After the first day, I should say the biggest and most important “get” is not about the productivity, freedom or flexibility, although they’re truly there, it’s about the trust I’ve never experienced at work.

“I trust you. So no matter what, when and where you do the work, I don’t care, as long as we’re moving forward.” That was pretty much the key conversation I had. It was a really warm feeling. I don’t know how to explain it in words exactly, to be honest I did see that remote work coming before I joined. But when it really happened, I was flattered! I’ve never got this kind of tremendous trust, it means a lot to me as a person. I’m sure I’m not gonna waste it.

Trust is the first step to remote work. It’s personal and fundamental. If you trust your teammate, with proper tools and communications I really think remote work will work!

Install capybara-webkit on Mavericks

I got a brand new Mac Air recently, when I tried to install capybara-webkit I encountered some problems.

The Error

Got this error when bundle install one rails app.

1
2
3
4
5
6
7
8
9
10
11
Installing capybara-webkit (0.12.1)
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

    /Users/qihuan-piao/.rvm/rubies/ruby-2.0.0-p247/bin/ruby extconf.rb


Gem files will remain installed in /Users/qihuan-piao/.rvm/gems/ruby-2.0.0-p247/gems/capybara-webkit-0.12.1 for inspection.
Results logged to /Users/qihuan-piao/.rvm/gems/ruby-2.0.0-p247/gems/capybara-webkit-0.12.1/./gem_make.out

An error occurred while installing capybara-webkit (0.12.1), and Bundler cannot continue.
Make sure that `gem install capybara-webkit -v '0.12.1'` succeeds before bundling.

Tried brew install qt didn’t get luck, raised this error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
brew install qt4 --build-from-source
==> Downloading http://download.qt-project.org/official_releases/qt/4.8/4.8.5/qt-everywhere-opensource-src-4.8.5.tar.gz
Already downloaded: /Library/Caches/Homebrew/qt-4.8.5.tar.gz
==> ./configure -prefix /usr/local/Cellar/qt/4.8.5 -system-zlib -confirm-license -opensource -nomake demos -nomake examples -cocoa -fast -release -no-3dnow -L/opt/X11/lib -I/opt/X11/include -pl
==> make
                                   ^
7 errors generated.
make[2]: *** [.obj/release-shared/qdrawhelper_ssse3.o] Error 1
make[1]: *** [release] Error 2
make: *** [sub-gui-make_default-ordered] Error 2

READ THIS: https://github.com/mxcl/homebrew/wiki/troubleshooting

These open issues may also help:
    https://github.com/mxcl/homebrew/pull/22283
    https://github.com/mxcl/homebrew/issues/23480

Solution

Checked all those links and tried several things, eventually downloading qt installer from the qt-project website solved my problem.

Download link

Just install the debug libraries, qt libraries then bundle install again.

Note

If you already got qt installed and capybara-webkit worked before upgrade to Mavericks, then it’s probably gonna work as well.

Install Ruby 1.9.3 on Mavericks

Problem

1
2
3
4
5
6
7
8
9
10
$ rvm install 1.9.3

Searching for binary rubies, this might take some time.
Found remote file https://rvm.io/binaries/osx/10.9/x86_64/ruby-1.9.3-p448.tar.bz2
Checking requirements for osx.
Installing requirements for osx.
Updating system - using Zsh, can not show progress, be patient...
Error running 'requirements_osx_brew_update_system ruby-1.9.3-p448',
please read /Users/qihuan-piao/.rvm/log/1383014621_ruby-1.9.3-p448/update_system.log
Requirements installation failed with status: 1.

Solution

1
2
3
4
5
6
7
8
$ gcc -v

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

rvm install 1.9.3 --with-gcc=clang

Reference

http://stackoverflow.com/questions/8139138/how-can-i-install-ruby-1-9-3-in-mac-os-x-lion

Use HTTP Caching in Heroku with Cloudflare

TL;DR

  • Heroku doesn’t provide HTTP caching by default
  • Use CDN like Cloudflare or Amazon CloudFront
    • Clouldflare has free plan, takes you less than 3 minutes to setup.

HTTP Caching

Just a quick example of HTTP caching.

HTTP caching example

You can really feel the difference after you enabled it. Since most of your static assets, like js, css and image files, are gonna use browser cache without hitting your rails application, the user experience is improved a lot.

In my hobboy project quoty.me, user had to download the black colored background image(22k) every time they visit a page, so there was a time lag to notice the background changed from white to black, which bothered me a lot. And that was why I noticed the HTTP caching is not enabled in Heroku.

Heroku’s HTTP Caching

Heroku doesn’t provide HTTP caching by default. In order to take advantage of HTTP caching, you’ll need to configure your application to set the appropriate HTTP cache control headers and use a content delivery network (CDN) or other external caching service.

I’m using cedar stack in Heroku, but I have to use “external”(oppose to add-ons or solutions provided by Heroku) stuff to make HTTP caching work.

I’ve also tried Rack::Cache with Memcache but somehow it didn’t work for me.

Setup Cloudflare

This time I decided to give Cloudflare a try. It’s free, easy to setup, and just work.

I’ll cut off how to setup Cloudflare, as the tutorial in its website is realy excellent. Just go to https://www.cloudflare.com/ and follow the instructions, within 3-4 steps you’re done.

The other thing you have to do is to update your nameserver to the one provided by Cloundflare, like “IAN.NS.CLOUDFLARE.COM”. I’m using GoDaddy for my quoty.me domain, the link to setup nameserver looks like this.

GoDaddy Nameserver setup link

Further Reading

夜型の僕が早起きを31日間頑張って思ったこと

僕とチームメートのテーブルの間にポスト・イットを貼って早起きした日数を刻んでました。

TL;DR

  • 早起きしたいと自ら望むこと
  • やれば絶対誰でもできる(事実)
  • 柔軟に軌道修正

はじめに

5/22から早起きを頑張りました。 “何時に起きてるの?” “8時です” “えー全然早くね~じゃん” と即行で鼻で笑われたりしました。

はい、ここ数年は大体夜2時前後に寝て朝9時半起きだった僕にとっては もうこれが大きな一歩です。 何より、全然できないといつも思ってたからです。 また今はいろいろ試行錯誤で8時にしただけで、 しようと思えば6〜7時にもできる自信が付いたんです。

この一ヶ月で頑張ったこと、思ったことをの共有したいと思います。

きっかけ

段々年をとると、よほどインパクのでかい物事が起きないと 今までの生活習慣を変えるのは大変かなと思ってます。 その中でも早起きはずっとしたかったんです。 ちょうどチームの友達とこの話をしたら彼も同じことを考えるので 2人で頑張ろうという流れに何となくなりました。 できるかどうか、半信半疑の感じで始めたのですが、

  • 約束を破りたくない
  • そして何より負けたくない

の2つの見えないチカラが背中をずっと押してくれました。 (前半は↑ですね、大体途中でもう早起きが習慣になってきました)

早起きするために

まず”心の準備”

自発的に早起きしたい気持ち

  • 周りの家族や友達、上司に言われてから
  • 朝ミーティングがあるから

のような感じですと絶対長続きできないです。 自発的に”そういう人になりたい”と思わないと。

Be aware of yourself

自分の一日の行動、気持ち、体調などに気を配ることです。 効率は上がったのか?いつが一番眠いのか?気持ちいいと感じるのか? などなどどんどん自分に質問を投げて、軌道修正する。

メリットを感じなければやめる

実際早起きして得することはあったのか? 無駄に疲れて、ストレスが溜まるばかりにいいことは全然なかった。 特にメリットを感じなければやめて

具体的な試み

極めてシンプルです、簡単とは言えないですが。

  1. 監督しあう仲間を探す
  2. 朝食の時間を楽しむ
  3. 睡眠時間は削らない
  4. 運動して無理やり早寝する

監督(supervise)しあう仲間を探す

自分と同じくらいのレベルで同じ挑戦をしたい人を見つかればベストですが、 そうじゃなくても自分が一番信頼できる人に自分のプランをコミットするのも全然ありです。 こういう手法は今まで何度も聞きましたが、やってみて思ったのが 本当に一番効率のいい方法で、僕の性格にもぴったりの感じです。

朝食を楽しむ

早起きしたら朝食は絶対抜けないと自分で決めました。 たまには外で食べながら本を読んだり、単純にぼっとしたりして 自分なりで朝を楽しんでました。 余裕のある朝なんて贅沢すぎる!

また初めて最初の土日が一番ピーク(起きたくない)かもしれませんが、 あえて朝に予定を入れたり、起きたらとりあえず 一杯のコーヒーを飲んだりするなど自分なりで 乗り越える方法を事前に考えて起きたらいいです。

睡眠時間は削らない

早起きした分仕事も早めにスタートできるので、 夜は今までと比べて早めに退勤することができて、 帰って料理したり本読んだりすれば自然に11時くらいから もう寝るモードに入ります。 逆に睡眠時間を削ると本末転倒なことになるので、 疲れたり眠くなったりするしかないですね。

運動して無理やり早寝する

最初の二三日は僕はいつもランニングして”無理やり” 早寝を促進してます。 体がちょうど良い感じに疲れてその流れで寝れば 自然に早起きのサイクルになります。

その他のInspiration

#053: How to Become a Morning Person [Podcast] | Michael Hyatt

You said, “I’m a night person or a night owl by nature.” It’s my own personal belief that people aren’t morning people or evening people by nature. I don’t think there is anything biologically or physiologically that you can point to that would suggest that is somehow predetermined. We may have a preference and we may have a set of practices that have been conditioned into a habit over a long period of time, but these are things I believe that with focus and intention can be changed.

最後に

紹介した方法以外の目覚めし時計をどう設定するとか、 部屋の照明を暗くするとかは全部マイナーの問題にすぎないです。

新しいことを21日間続ければ習慣になるそうですが、 僕の場合大体10日でもう慣れた実感がしました。 ライフサイクルが改善されて、電車の中でも本が読める気力があって オフで勉強したことが仕事にもいいフィードバックと刺激になったと思います。 この際隣の人と試してみてはどうですか?

Notes on Let’s Make Testing Fun Again

The talk

Let’s Make Testing Fun Again at WindyCityRails 2012 | Lanyrd

Communication

Tests give you about the code. This happens over time, where you find that tests have become hard to write, the tests are becoming complicated and bogged down.

Rather than use it as an opportunity to blame the test or to blame testing, you should use it as an opportunity to learn something about your code, that there is a dependency that could potentially be simplified, or something you could be doing better.

Keep relevant setup close

This is something that “relative good”(in terms of “absolute good”). It’s generally good to keep the setup of your tests close to the tests where it is.

What you want to avoid is the case where some sort of before setup way at the top of the file and you have stuff, and more stuff, and more stuff, and eventually in the end, what’s the @user.name? I don’t remember.

An Example

1
2
3
4
5
6
7
8
9
10
11
12
13
describe User do
  before do
    @user = Factory.build(:user)
  end

  # and more stuff

  # and more stuff

  it "uses the user" do
    @user.name.should == # i don't remember
  end
end

This is where you see sometimes people say “You should never have setup outside your original test”.

What to do about duplications and complicate shared setups

I think a lot of times if you need a complicated setup to test code, that often means your code is too complicated you need to start working out to move your dependencies. I tend to have a higher tolerance for duplication in my tests than I do in my regular code, specifically because I’m more interested in having the communication close, and less interested in maybe being clever about extracting setups in my tests. Sometimes I do if there’s a piece of stuff that’s really tightly coupled, and I can give it a descriptive enough name that I still feel like I have the benefit of keeping the setup close in the actual test.

Test Simple Values

By using literal in check, when the spec fails, the error message is much easier to understand.

Spy, don’t mock

A jasmine test example.

1
2
3
4
5
6
7
8
9
10
11
var cheeseburger = {
  cheeses: function() {
    // Ajax call to cheese server
  }
};

it('spies on the cheese server', function() {
  spyOn(cheeseburger, 'cheeses');
  cheeseburger.cheeses();
  expect(cheeseburger.cheeses).toHaveBeenCalled();
});
  • Readability advantage
  • Easy to find where actually fails

BTW, if in spec, we need write like this:

1
2
3
4
5
it 'spies on the cheese server' do
  # in the reversed order
  cheeseburger.should_receive(:cheeses)
  cheeseburger.cheeses
end

More about Spy on Ruby and Rails

thoughtbot/bourne gem can let you have the rspec-like test spies syntax.

Spy vs spy, good explanation on what is test spy the benefit of it.

What Do We Love?

These are quite frank and interesting questions.

  1. Do we still love writing tests?
  2. Or do we just love having written tests?
  3. Or do we just love saying that we’ve written tests?

Notes on Fast Test, Slow Test

Recently I got lots of inspirations and thoughts on how to write good tests. Thanks to Cookpad, the company I’m working on, I get the chance to work on a very large scale rails app. But it also introduces me to some very bad tests. Sometimes I found myself so difficult to add any spec, as it needs lots of extra efforts to make the fixture data, also one model got too many dependencies and collaborators, etc. I know it’s wrong, the code smells, just don’t know where to start.

So when I watched this video “Fast Test, Slow Test” by @garybernhardt, who also runs DestroyAllSoftware screencast, I found it very helpful and wanted to take some notes on it.

Three Goals in Testing

  • Prevent Regression
  • Prevent Fear
  • Prevent Bad Design

How to Fail in Testing

  • Selenium as primary testing
  • “Units Test” are too large
  • Fine-grained tests around legacy code

What you’re saying here is that we acknowledge that this code sucks, so we’re going to go in and write tight tests around it that solidifies interface and just bake the badness forever. This is the worst way to do unit testing, go in your legacy system and write test around bad code.

Too many dependencies in test

You end up with a test suite where it tends to either completely succeed or completely fail. It doesn’t give you any fine-grained feedback about what has actually gone wrong it just tells you you broke something. And you are left to dig these stack traces. In the ideal test suite you don’t have to dig through stack traces because ideally once test fails it will tell you exactly was broken (of course you never achieve that)

More than 8 lines for a model test

I would ask myself why do I need to setup so much of the world to test this one small piece of behavior. That causes me to decompose it which causes better system design.

10% System/Acceptance Test, 90% Unit Test

(in terms of test cases, not lines of code)

That applies to mostly object heavy system like web app, that have a lot of logic and not a lot of boundaries.

Advice on models that very tied to database

Question

If you’re working with model objects that they’re very tied to the database specifically things like you have to save that one object before you can associate with the other object and that means hitting the database. What’s your advice in terms of writing fast unit test?

Answer

Take all of that behavior that’s on your model objects and pull out of their into service classes that are stateless, that interact with the model objects. So view calls a service, service contains the intelligence and the service manipulates the model objects.

You can still have methods on your model objects do things like wrap specific queries in class methods, or wrap specific mutations when multiple fields are commonly manipulated together.

It’s okay to have those on your model object and then have the service use those but if you pull the behavior of the system out into the service, and you control the boundary between the service in the model by those methods you’ve written on the model, then you can mock out more easily and safely.