###

alias hbin me

初识 Circus (1)

Circus 是用 Python 编写的一个用来监控和管理 Processes 与 Sockets 的工具。

预备环境

  • Ubuntu 12.04 LTS 或者更高
  • Python 2.7 with pip installed

安装

  1. 首先,通过 PPA 安装:

    1
    2
    3
    
    $ sudo add-apt-repository ppa:roman-imankulov/circus
    $ sudo apt-get update
    $ sudo apt-get install circus
    

  2. PPA 中的版本比较低(0.7.0),再通过 pip 升级:

    1
    
    $ sudo pip install -U circus
    

  3. 修改 /etc/init/circus.conf 文件为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    description "circusd"
    
    start on (net-device-up
             and local-filesystems
             and runlevel [2345])
    stop on runlevel [!2345]
    
    respawn
    
    exec /usr/local/bin/circusd \
           –log-level debug \
           –log-output /var/log/circus.log \
           –pidfile /var/run/circusd.pid \
           /etc/circus/circusd.ini
    

    重启服务 sudo service circus restart

    注意,这里原先的 /usr/bin/circusd 已经被我们改为了 /usr/local/bin/circusd,这是因为通过 pip 升级后地址改变了。

    之所以先用 PPA 安装再通过 pip 升级,是因为 PPA 安装之后,系统会帮我们设置好 Upstart/Services,非常方便。包括以下三个文件:

    • /etc/circus/circusd.ini
    • /etc/init.d/circus
    • /etc/init/circus.conf

Upstart 文件介绍:https://www.digitalocean.com/community/tutorials/the-upstart-event-system-what-it-is-and-how-to-use-it

一个简短的 Python 应用示例

  1. 新建一个 Python 程序:/path/to/the/myprogram.py

    1
    2
    3
    4
    5
    
    import os
    from datetime import datetime
    
    with open(os.path.join(os.path.dirname(file), 'myprogram.log'), 'a') as f:
        f.write("{}: myprogram is running!\n".format(datetime.now()))
    

  2. 新建一个配置文件:/etc/circus/conf.d/myprogram.ini

    1
    2
    
    [watcher:myprogram]
    cmd = python /path/to/the/myprogram.py
    

  3. 重启 Circus 服务

    1
    
    $ sudo service circus restart
    

  4. 通过 tail -f /path/to/the/myprogram.log,观察运行。

一个简短的 Flask 应用示例

  1. 新建一个 Flask 应用:webapp.py,放在 /path/to/myapps/ 下

    1
    2
    3
    4
    5
    6
    7
    
    from flask import Flask
    
    app = Flask(name)
    
    @app.route('/')
    def index():
       return 'Circus Awesome!'
    

  2. 安装 virtualenv 与 Flask

    1
    2
    3
    4
    
    $ cd /path/to/myapps
    $ virtualenv venv
    $ source venv/bin/activate
    $ pip install flask chaussette
    

    Chaussette 是一个 WSGI 服务器。

  3. 新建一个配置文件:/etc/circus/conf.d/webapp.ini

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    [watcher:webapp]
    copy_env = True
    virtualenv = /path/to/myapps/venv
    working_dir = /path/to/myapps
    
    use_sockets = True
    cmd = chaussette webapp.app
    args = –fd $(circus.sockets.webapp)
    numprocesses = 5
    
    [socket:webapp]
    host = 0.0.0.0
    port = 5000
    

  4. 重启 Circus 服务

    1
    
    $ sudo service circus restart
    

  5. 访问 http://localhost:5000/

Rails raw, html_safe vs html_escape(h) and benchmark

  • raw is a wrapper around String#html_safe.
  • String#html_safe just returns an instance of ActiveSupport::SafeBuffer.

@Daniel wrote a post about when to use raw() and when to use .html_safe

  • html_escape originally defined in ERB::Util.html_escape, also aliased as: h

There are several html escaption methods, here is the benchmark:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
require 'benchmark/ips'
require 'open-uri'

require 'cgi'
require 'erb'
require 'rack'

puts "===== Short String =====\n\n"

Benchmark.ips do |x|
  SHORT_STR = %(<html><head></head><body></body></html>)

  x.report 'CGI::escapeHTML' do
    CGI::escapeHTML SHORT_STR
  end

  x.report 'ERB::Util.html_escape' do
    ERB::Util.html_escape SHORT_STR
  end

  x.report 'Rack::Utils.escape_html' do
    Rack::Utils.escape_html SHORT_STR
  end

  x.compare!
end

puts "===== Long String =====\n\n"

Benchmark.ips do |x|
  LONG_STR  = open('http://example.com/').read

  x.report 'CGI::escapeHTML' do
    CGI::escapeHTML LONG_STR
  end

  x.report 'ERB::Util.html_escape' do
    ERB::Util.html_escape LONG_STR
  end

  x.report 'Rack::Utils.escape_html' do
    Rack::Utils.escape_html LONG_STR
  end

  x.compare!
end

require 'active_support/core_ext/string'

puts "===== Short html safe string =====\n\n"

Benchmark.ips do |x|
  SHORT_HTML_SAFE_STR = %(<html><head></head><body></body></html>).html_safe

  x.report 'CGI::escapeHTML' do
    CGI::escapeHTML SHORT_HTML_SAFE_STR
  end

  x.report 'ERB::Util.html_escape' do
    ERB::Util.html_escape SHORT_HTML_SAFE_STR
  end

  x.report 'Rack::Utils.escape_html' do
    Rack::Utils.escape_html SHORT_HTML_SAFE_STR
  end

  x.compare!
end

puts "===== Long html_safe String =====\n\n"

Benchmark.ips do |x|
  LONG_HTML_SAFE_STR  = open('http://example.com/').read.html_safe

  x.report 'CGI::escapeHTML' do
    CGI::escapeHTML LONG_HTML_SAFE_STR
  end

  x.report 'ERB::Util.html_escape' do
    ERB::Util.html_escape LONG_HTML_SAFE_STR
  end

  x.report 'Rack::Utils.escape_html' do
    Rack::Utils.escape_html LONG_HTML_SAFE_STR
  end

  x.compare!
end

__END__


===== Short String =====
Comparison:
ERB::Util.html_escape: 113217.7 i/s
CGI::escapeHTML: 110218.2 i/s - 1.03x slower
Rack::Utils.escape_html: 81503.8 i/s - 1.39x slower

===== Long String =====
Comparison:
ERB::Util.html_escape: 25110.7 i/s
CGI::escapeHTML: 24430.1 i/s - 1.03x slower
Rack::Utils.escape_html: 16207.2 i/s - 1.55x slower

===== Short HTML Safe String =====
Comparison:
ERB::Util.html_escape: 2772776.1 i/s
CGI::escapeHTML: 106256.2 i/s - 26.10x slower
Rack::Utils.escape_html: 72086.8 i/s - 38.46x slower

===== Long HTML Safe String =====
Comparison:
ERB::Util.html_escape: 2749941.1 i/s
CGI::escapeHTML: 24777.1 i/s - 110.99x slower
Rack::Utils.escape_html: 16229.5 i/s - 169.44x slower

Rails HTTP Request IDs

The HTTP Request IDs makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog.

Rails 3.2 introduced the ActionDispatch::RequestId middleware that make a unique X-Request-Id header avariable to the response.

For example: curl -I http://sample.dev/

1
2
3
4
5
6
HTTP/1.1 200 OK
...
X-Request-Id: ddaa28e2-3395-4e66-9ca7-48480882a1df
X-Runtime: 0.045724
Date: Fri, 04 Jul 2014 03:23:43 GMT
Connection: close

To show this request ID in with your application logs, add this line to your config/environments/production.rb

1
config.log_tags = [:uuid]

The logs will then be tagged with the Request ID:

1
2
3
4
5
6
7
I, [2014-07-04T11:23:43.752028 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df] Started HEAD "/" for 127.0.0.1 at 2014-07-04 11:23:43 +0800
I, [2014-07-04T11:23:43.755366 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df] Processing by StaticPagesController#home as */*
I, [2014-07-04T11:23:43.759832 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df]   Rendered static_pages/home.html.erb within layouts/application (0.2ms)
I, [2014-07-04T11:23:43.791258 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df]   Rendered layouts/_shim.html.erb (0.0ms)
I, [2014-07-04T11:23:43.793947 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df]   Rendered layouts/_header.html.erb (0.3ms)
I, [2014-07-04T11:23:43.796489 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df]   Rendered layouts/_footer.html.erb (0.2ms)
I, [2014-07-04T11:23:43.797112 #33305]  INFO -- : [ddaa28e2-3395-4e66-9ca7-48480882a1df] Completed 200 OK in 42ms (Views: 41.2ms | ActiveRecord: 0.0ms)

References:

  1. http://guides.rubyonrails.org/3_2_release_notes.html#action-dispatch
  2. https://devcenter.heroku.com/articles/http-request-id

Resolving Vim Key Mapping Conflict

Ack.vim is a plugin for the Perl CLI script ack which is a replacement of grep. It provides a front for running ack from vim.

By default, It will search recursively under the current directory. It’s not convenient to search of a project.

Fortunately, there is a plugin vim-rooter which will changes the working directory to the project root automatically.

I use Janus for my Vim. So I git clone it to ~/.janus directory. It works great, but if I open Vim from the terminal, I got a key mapping conflict error message:

1
2
3
Error detected while processing /Users/hbin/.janus/vim-rooter/plugin/rooter.vim:
line  159:
E227: mapping already exists for ,cd

That’s because Janus mapped <leader>cd to changes the path to the active buffer’s file, and the vim-rooter also try to map <leader>cd to <Plug>RooterChangeToRootDirectory.

Here is the source

1
2
3
if !hasmapto("<Plug>RooterChangeToRootDirectory")
  map <silent> <unique> <Leader>cd <Plug>RooterChangeToRootDirectory
endif

The Solution is simple, just create a mapping to <Plug>RooterChangeToRootDirectory:

1
nnoremap <leader>cr <Plug>RooterChangeToRootDirectory

References:

  1. http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_1)
  2. http://stackoverflow.com/questions/3776117/what-is-the-difference-between-the-remap-noremap-nnoremap-and-vnoremap-mapping

Enjoy!

The smart-shift package released!

Smart Shift is a minor mode for conveniently shift the line/region to the left/right by the current major mode indentation width.

Installation

Melpa

Once you have setup Melpa you can use package-install command to install. The package name is smart-shift.

Manual

1
2
3
(add-to-list 'load-path "/path/to/smart-shift")
(require 'smart-shift)
(global-smart-shift-mode 1)

Customizing

Smart Shift will infer the indentation level of current major mode, if none of major modes listed below match, use the tab-width as default.

It can also be set to a number explictly.

1
(setq smart-shift-indentation-level 2)

Or, for some major mode we haven’t support, add following snippets to your config file. Test it and send a PR.

1
2
3
4
(eval-after-load 'your-major-mode
  '(progn
     (add-to-list 'smart-shift-mode-alist
                  '(major-mode-or-derived-mode . customize-base-offset))))

Supported major modes

  • lisp-mode
  • emacs-lisp-mode
  • c-mode
  • c++-mode
  • objc-mode
  • java-mode
  • idl-mode
  • pike-mode
  • awk-mode
  • ruby-mode
  • python-mode
  • swift-mode
  • js-mode
  • js2-mode
  • coffee-mode
  • css-mode
  • scss-mode
  • slim-mode
  • html-mode
  • web-mode
  • sh-mode
  • yaml-mode
  • text-mode
  • markdown-mode
  • fundamental-mode

Interactive commands

Command Keybinding Description
smart-shift-left C-c [ Shift the line or region ARG times to the left.
smart-shift-right C-c ] Shift the line or region ARG times to the right.

After invoking smart-shift-left or smart-shift-right the first time, you can simply hit [ or ] to continuously shift to left or right, respectively.

If you use the key-chord like me. I strongly recommend you add the following snippets:

1
2
(key-chord-define-global "<<" 'smart-shift-left)
(key-chord-define-global ">>" 'smart-shift-right)

Contribute

Repo is here, forks and pull requests are welcome!

Ubuntu Bootstrap

Vagrant plugin - vbguest

Yestoday, I upgraded my VirtualBox from version ‘4.2.18’ to ‘4.3.6 along with upgraded the vagrant to '1.4.3’.

After I run vagrant up I got a warning message:

1
2
3
4
5
6
7
8
9
[default] The guest additions on this VM do not match the installed version of
VirtualBox! In most cases this is fine, but in rare cases it can
prevent things such as shared folders from working properly. If you see
shared folder errors, please make sure the guest additions within the
virtual machine match the version of VirtualBox you have installed on
your host and reload your VM.

Guest Additions Version: 4.2.18
VirtualBox Version: 4.3

After a google, I found that there is a plugin which can automatically installs the host’s VirtualBox Gest Additions on the gest system.

Here is the repo address: https://github.com/dotless-de/vagrant-vbguest

And that solved my problem.

Handle 301 Redirection in Ruby

While I send a GET request to some URI, I got a 301 MOVED PERMANENTLY response returned.

Here is the code:

1
2
require 'net/http'
response = Net::HTTP.get_response(URI(http://***/xyz))

The response’s status code is 301 and body is a empty string.

Solution

  • Use open-uri, open-uri handles redirects automatically.
1
2
require 'open-uri'
response = open('http://***/xyz')
  • Handle redirects with Net::HTTP
1
2
3
4
5
6
7
def get_response_with_redirect(uri)
   r = Net::HTTP.get_response(uri)
   if r.code == "301"
     r = Net::HTTP.get_response(URI.parse(r.header['location']))
   end
   r
end
  • Use the correct URI

The reason that the URI returns a 301 redirection is the trailing slashes are misused. Historically, it’s common for URLs with a trailing slash to indicate a directory, and those without a trailing slash to denote a file:

1
2
http://example.com/foo/ (with trailing slash, conventionally a directory)
http://example.com/foo  (without trailing slash, conventionally a file)

References:

  1. http://stackoverflow.com/questions/7210232/ruby-nethttp-following-301-redirects
  2. http://stackoverflow.com/questions/5948659/trailing-slash-in-urls-which-style-is-preferred

The Ultimate Solution of Emacs Finding Tags in a Rails Project

Emacs is my favorite text editor. I do a lot of Ruby on Rails programming using it. It’s really handy, useful and hacky for me ;)

Sometimes, I want to jump to the definition of a method or a class/module, I will use the ctags tool to build a TAGS file in the root of my project.

1
ctags -e -R --extra=+fq --exclude=db --exclude=doc --exclude=log --exclude=tmp --exclude=.git --exclude=public . $(rvm gemdir)/gems

The appending $(rvm gemdir)/gems will including Rails build-in method(or class/module) definitions, it’s very useful to browse Rails source code.

Formerly, I use the Emacs build-in find-tag command which bounding to M-. to find the tags. It works, but what annoy me is that everytime I find-tag, it prompt to choose a tag, even the first one always what I want. So I write a command to take the place of default find-tag:

1
2
3
4
(defun hbin-find-tag ()
  (interactive)
  (find-tag (find-tag-default)))
(global-set-key (kbd "M-.") 'hbin-find-tag)

It seems that things goes well. But I found that If a method defined in many places. find-tag only jump to one of them, and without any prompt. This misguide me occasionally.

After some google search, I found a package Etags-Select provides a feature to find tag from multiple tag files, and if there are multiple matching tags, it will open a selection window for you to choose the one you want.

Sounds great! eh?

After give it a try, I found it’s not that perfect(not perfect for ruby code at least). It can’t jump to the definition of a method whose name ending with a question mark! e.g. signed_in?. After a wandering around the source code of Etags-Select, I decide to ‘fix’ it by myself. And finally, I made it! Yeah!

It’s showtime ;)

Firstly, git clone my forked Etags-Select version and add it to your Emacs load-path, you may also need to install eproject and s.el first.

1
2
3
;;; Installation of Etags-select
(add-to-list 'load-path "/path/to/my-etags-select")
(require 'etags-select)

Secondly, add following snippets to your emacs dotfile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
(require 'thingatpt)

(defun thing-after-point ()
  "Things after point, including current symbol."
  (if (thing-at-point 'symbol)
      (save-excursion
        (let ((from (beginning-of-thing 'symbol))
              (to   (end-of-thing 'line)))
          (and (> to from)
               (buffer-substring-no-properties from to))))))

(defun ruby-thing-at-point ()
  "Get ruby thing at point.
   1. thing at 'current_user'   get current_user;
   2. thing at '!current_user'  get current_user;
   3. thing at 'current_user!'  get current_user!;
   4. thing at 'current_user='  get current_user=;
   5. thing at 'current_user =' get current_user=;
   6. thing at 'current_user ==' get current_user;
   7. thing at 'current_user ||=' get current_user=;
   Otherwise, get `find-tag-default symbol."
  (if (member (symbol-name major-mode)
              '("ruby-mode" "rhtml-mode" "haml-mode" "slim-mode"))
      (let ((symbol (thing-at-point 'symbol))
            (remain (thing-after-point)))
        (if (and symbol remain)
            (let ((sym (s-chop-prefixes '("!!" "!") symbol))
                  (rem (s-chop-prefixes '("!!" "!") remain)))
              (if (s-matches? (concat "^" sym "\\( *\\(||\\)?=[^=]\\)") rem)
                  (concat sym "=")
                sym))
          (find-tag-default)))
    (find-tag-default)))

(defun visit-project-tags ()
  (let ((tags-file (concat (eproject-root) "TAGS")))
    (visit-tags-table tags-file)
    (message (concat "Loaded " tags-file))))

(defun hbin-build-ctags ()
  "Build ctags file at the root of current project."
  (interactive)
  (let ((root (eproject-root)))
    (shell-command
     (concat "ctags -e -R --extra=+fq "
             "--exclude=db --exclude=doc --exclude=log --exclude=tmp --exclude=.git --exclude=public "
             "-f " root "TAGS " root)))
  (visit-project-tags)
  (message "TAGS built successfully"))

(defun hbin-etags-find-tag ()
  "Borrow from http://mattbriggs.net/blog/2012/03/18/awesome-emacs-plugins-ctags/"
  (interactive)
  (if (file-exists-p (concat (eproject-root) "TAGS"))
      (visit-project-tags)
    (hbin-build-ctags))
  (etags-select-find (ruby-thing-at-point)))

(global-set-key (kbd "M-.") 'hbin-etags-find-tag)

Thirdly, The Forgotten snippets

1
2
3
4
5
;; Modify syntax entry
(defun hbin-ruby-mode-init ()
  (modify-syntax-entry ?? "w")
  (modify-syntax-entry ?! "w"))
(add-hook 'ruby-mode-hook 'hbin-ruby-mode-init)

Notice!! According to which template language you prefer, you may need to modify-syntax-entry for that major mode. For example to rhtml

1
2
3
4
(defun hbin-rhtml-mode-init ()
  (modify-syntax-entry ?? "w")
  (modify-syntax-entry ?! "w"))
(add-hook 'rhtml-mode-hook 'hbin-rhtml-mode-init)

DONE and ENJOY IT!

Let me show you some scenes.

1
2
3
4
5
6
7
8
9
current_user              # jump to current_user definition
!current_user.nil?        # jump to current_user definition
current_user == User.find # jump to current_user definition

signed_in?                # jump to signed_in? definition
self.current_user = nil   # jump to current_user= definition
current_user ||= User.new # jump to current_user= definition

current_user!             # jump to current_user! definition

It’s awesome! Cheer up!

At last

I’m not good at writing emacs lisp code, any bug report and bug fix pull request are welcome!

Cleanup The Emacs Mode Line

Look at the bottom of the Emacs frame, there is a distinctive line above the minibuffer, it’s mode-line. This displays various information about what is going on in the buffer, such as whether there are unsaved changes, the editing modes that are in use, the current line number, and so forth.

After installed several add-ons, you may find that the mode-line occupied by the minor-mode indicators. As follow:

Not all of the indicators are useful for me, even more, they (but not the minor mode) pollute the mode-line, so I want to save the limited spaces.

  • DIY

    First, you should add this function to your .emacs.

1
2
3
(defun hbin-remove-mm-lighter (mm)
  "Remove minor lighter from the mode line."
  (setcar (cdr (assq mm minor-mode-alist)) nil))

Then, customize the minor mode as follow:

1
2
3
4
(hbin-remove-mm-lighter 'autopair-mode)
(hbin-remove-mm-lighter 'textmate-mode)
(hbin-remove-mm-lighter 'eproject-mode)
(hbin-remove-mm-lighter 'whole-line-or-region-mode)
  • Diminish plugin

    diminish is a package that diminishes the amount of space taken on the mode line by the names of minor modes. After installed it handy, costomize as follow:

1
2
3
4
(eval-after-load "autopair" '(diminish 'autopair-mode))
(eval-after-load "textmate" '(diminish 'textmate-mode))
(eval-after-load "eproject" '(diminish 'eproject-mode))
(eval-after-load "whole-line-or-region" '(diminish 'whole-line-or-region-mode))

END!