node.js のインストール - Ubuntu 10.04 編

Written by @dr_taka_n at 2012/03/24 18:45 [, ]

Ubuntu 10.04 への node.js のインストールメモ。

apt repository を新規に追加してインストール。

$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get -y update
$ sudo apt-get -y install nodejs

これだけ。。

irb でのオブジェクトの表示を更に見易く - awesome print

Written by @dr_taka_n at 2012/03/20 23:50 [, ]

環境を整理していていたついでにメモを残しておく。

irb でシンタックスハイライトなどを行うには Wirble など便利な gem があるが、awesome_print はオブジェクトのデータ構造もわかりやすくインデントして表示してくれる。

Awesome Print is a Ruby library that pretty prints Ruby objects in full color exposing their internal structure with proper indentation. Rails ActiveRecord objects and usage within Rails templates are supported via included mixins.

インストールと irb でのお試し

インストール。

$ gem install awesome_print
Fetching: awesome_print-1.0.2.gem (100%)
Successfully installed awesome_print-1.0.2
1 gem installed
Installing ri documentation for awesome_print-1.0.2...
Installing RDoc documentation for awesome_print-1.0.2...

irb で試してみる。

$ irb
1.9.3-p125 :001 > require 'awesome_print'
 => true 
1.9.3-p125 :002 > data = [ false, 42, %w(forty two), { :now => Time.now, :class => Time.now.class, :distance => 42e42 } ]
 => [false, 42, ["forty", "two"], {:now=>2012-03-20 14:11:20 +0900, :class=>Time, :distance=>4.2e+43}] 
1.9.3-p125 :003 > ap data
[
    [0] false,
    [1] 42,
    [2] [
        [0] "forty",
        [1] "two"
    ],
    [3] {
             :now => 2012-03-20 14:11:20 +0900,
           :class => Time < Object,
        :distance => 4.2e+43
    }
]
 => [false, 42, ["forty", "two"], {:now=>2012-03-20 14:11:20 +0900, :class=>Time, :distance=>4.2e+43}] 

awesome_print を require 後に、ap メソッドに awesome_print で表示させてたいオブジェクトを預ける。

実際のコンソールの画面も貼りつけておく。

awesome_print-1

irb での出力を常に awesome_print で

先ほどの確認では、awesome_print を使って表示するためには ap にオブジェクトを預ける必要があったが、ap の入力なしに常に awesome_print で表示が行えるようにする。

$HOME/.irbrc に以下を追記。
($HOME はユーザアカウントの HOME 直下。)

$HOME/.irbrc:

# load libraries
require 'rubygems'
require "awesome_print"

unless IRB.version.include?('DietRB')
  IRB::Irb.class_eval do
    def output_value
      ap @context.last_value
    end
  end
else # MacRuby
  IRB.formatter = Class.new(IRB::Formatter) do
    def inspect_object(object)
      object.ai
    end
  end.new
end

試してみる。

$ irb
1.9.3-p125 :001 > data = [ false, 42, %w(forty two), { :now => Time.now, :class => Time.now.class, :distance => 42e42 } ]
[
    [0] false,
    [1] 42,
    [2] [
        [0] "forty",
        [1] "two"
    ],
    [3] {
             :now => 2012-03-20 14:17:37 +0900,
           :class => Time < Object,
        :distance => 4.2e+43
    }
]

実際のコンソールの画面。 awesome_print-2

rails console でも awesome_print

Rails の console でも awesome_print を使いたくなる。

Rails アプリケーションの Gemfileawesome_print を追加してロードできるようにしておく。 開発環境とテスト環境のみで利用可能に。

$RAILS_HOME/Gemfile:

group :development, :test do
  gem 'awesome_print', '~> 1.0.2'
end

Bundler でインストールしておく。

$ bundle install

以下は Refinery CMS で使用した例。

モデルオブジェクトを見てみる。

$ rails c
Loading development environment (Rails 3.2.2)
1.9.3p125 :001 > Refinery::Page
class Refinery::Page < Refinery::Core::BaseModel {
                     :id => :integer,
              :parent_id => :integer,
                   :path => :string,
                   :slug => :string,
           :show_in_menu => :boolean,
               :link_url => :string,
             :menu_match => :string,
              :deletable => :boolean,
                  :draft => :boolean,
    :skip_to_first_child => :boolean,
                    :lft => :integer,
                    :rgt => :integer,
                  :depth => :integer,
          :view_template => :string,
        :layout_template => :string,
             :created_at => :datetime,
             :updated_at => :datetime
}

実際のコンソールの画面。 awesome_print-3

検索結果は以下のような感じ。

1.9.3p125 :002 > Refinery::Page.find(:first)
  Refinery::Page Load (0.5ms)  SELECT "refinery_pages".* FROM "refinery_pages" LIMIT 1
  Refinery::Page::Translation Load (0.3ms)  SELECT "refinery_page_translations".* FROM "refinery_page_translations" WHERE "refinery_page_translations"."refinery_page_id" = 1
#<Refinery::Page:0xa3f780c> {
                     :id => 1,
              :parent_id => nil,
                   :path => "Home",
                   :slug => "home",
           :show_in_menu => true,
               :link_url => "/",
             :menu_match => "^/$",
              :deletable => false,
                  :draft => false,
    :skip_to_first_child => false,
                    :lft => 1,
                    :rgt => 4,
                  :depth => 0,
          :view_template => nil,
        :layout_template => nil,
             :created_at => Sun, 18 Mar 2012 04:20:54 UTC +00:00,
             :updated_at => Sun, 18 Mar 2012 12:27:27 UTC +00:00
}

実際のコンソールの画面。 awesome_print-4

Refinery CMS v2 変更点

Written by @dr_taka_n at 2012/03/18 23:30 [, , , ]

2月後半に Refinery CMS のメジャーバージョンアップがされている。

Refinery 2.0 makes a number of very important updates to the project. Chief amongst these, Refinery now supports Rails 3.2 and the asset pipeline, and is now mountable as a Rails engine.

Rails 3.2 への対応が行われており、Ruby は 1.9.3 が推奨となっている。

Refinery CMS 自体の仕組みでは、軽く触ってみたところ、名前空間の扱い、設定情報の扱いが大きく変わっていた。 Changelog - resolve/refinerycms Wiki - GitHub から変更点をまとめておく。

  • ワークフローの変更
    • extension のインストールには、rake db:seed の実行が必要
    • 設定情報 (Settings) は、config/initializers/refinery ディレクトリでカスマイズする(admin 機能でない)
    • Settings extension は Refiney Core には含まれなくなった
  • Extensions の変更
    • extension の生成書式の変更
    • Settings (設定情報) は RefinerySetting で扱われなくなった
    • 公式(デフォルトで組み込まれている)の extension のネームスペースの変更

ワークフローの変更

extension のインストールには、rake db:seed の実行が必要

extension のインストールには、rake db:seed の実行が必要となっている。 extension を作成する際の適切なワークフロー以下の通りとなる。

# Add extension to your Gemfile
rails g refinery:<your_extension>
rake db:migrate
rake db:seed

設定情報 (Settings) は、config/initializers/refinery ディレクトリでカスマイズする(admin 機能でない)

Refinery アプリケーションの全ての設定は、config/initializers/refinery 内で行われる。 extension を作成した際には、上記ディレクトリ内にテンプレートから作成された設定がコピーされる。

Settings extension は Refiney Core には含まれなくなった

これまで Settings extension は Refinery Core に含まれてきていたが、Core から外れた。 別途 extension として導入は可能。

Extensions の変更

extension の生成書式の変更

v2.0 からは以下の書式で生成。

rails generate refinery:engine <model_name> field:type field:type field:type

より詳細なオプションは、

rails generate refinery:engine

で確認できる。

Settings (設定情報) は RefinerySetting で扱われなくなった

Settings (設定情報)は RefinerySetting からアクセスしていたが、使用されなくなり、代わりに Refinery::<ExtensionNamespace>.value でアクセスする。

例えば、Prtfolio extension では、Refinery::Portfolio.approximate_ascii_for_i18n でアクセスを行う。

I18n などでも、

> Refinery::I18n.default_locale
 => :ja 

という感じ。

設定情報は、内部的には 2つの別のファイルの extension 内で扱われている。
1つは、lib/refinery/<extension_name>/configuration.rb の中でデフォルトの値が宣言されており、 もう1つは、extension 生成時に generator から読み取られる lib/generators/refinery/<extension_name>/templates/config/initializers/<extension_name>.rb.erb 内にテンプレートが記載されている。

例えば、I18n の場合は、

lib/refinery/i18n/configuration.rb(デフォルト値の設定):

module Refinery
  module I18n
    include ActiveSupport::Configurable

    config_accessor :current_locale, :default_locale, :default_frontend_locale,
                    :enabled, :frontend_locales, :locales

    self.enabled = true
    self.default_locale = :en
    self.default_frontend_locale = self.default_locale
    self.current_locale = self.default_locale
    self.frontend_locales = [self.default_frontend_locale]
    self.locales = self.built_in_locales
  end
end

lib/generators/refinery/templates/config/initializers/refinery/i18n.rb.erb(テンプレートの設定):

# encoding: utf-8

Refinery::I18n.configure do |config|
  # config.enabled = <%= Refinery::I18n.config.enabled.inspect %>

  # config.default_locale = <%= Refinery::I18n.config.default_locale.inspect %>

  # config.current_locale = <%= Refinery::I18n.config.current_locale.inspect %>

  # config.default_frontend_locale = <%= Refinery::I18n.config.default_frontend_locale.inspect %>

  # config.frontend_locales = <%= Refinery::I18n.config.frontend_locales.inspect %>

  # config.locales = <%= Refinery::I18n.config.locales.inspect %>
end

となっている。

Nginx の設定概要

Written by @dr_taka_n at 2012/03/11 13:30 [, ]

Nginx に関して、とりあえず最初の一歩としてこれだけ知っておけば何とかなる、という内容をまとめておきたくメモを残すことにした。

Basic Nginx Configuration – Linode Library のページがとてもよくまとめられていたので、このページの内容をベースに書かせてもらった。

  • 全体的な構成
  • グローバルな設定
  • サーバの設定 - server ディレクティブ
    • listen ポートの設定 - listen ディレクティブ
    • バーチャルホストの設定 - server_name ディレクティブ
    • リソース(ロケーション)の設定 - location ディレクティブ
  • 設定ファイルの管理

全体的な構成

Nginx の設定に関しては、まずは、ディレクティブブロックコンテキストという言葉をおさえておく。

Nginx はインストール直後ですぐに使える状態になっており、設定は $NGINX_ROOT/conf/nginx.conf に記載されている。($NGINX_ROOTは Nginx がインストールされているルートディレクトリに読み替えること。)

その設定ファイル内のざっくりとした構成は以下のような感じになっている。

#user  nobody;
worker_processes  1;
...
events {
  ...
}

http {
  ...
  server {
    ...
    location / {
      ...
    }
    ...
  }
  ...
}

worker_processeseventshttpserverocation というのがディレクティブであり、eventshttpserverlocation などは{ } (中括弧)で囲まれたブロックを引数に持っている。 このブロックを含めたディレクティブの引数に Nginx で記載可能なシンタックスを使用して条件文などを混じえてディレクティブを記載していく。

Nginx はネストされたブロックの構文を使用でき、上記のように http ディレクティブserver ディレクティブがネストされている構文をとれる。この時、server ディレクティブhttp ディレクティブコンテキストにあると言う。

ディレクティブは、コンテキストによって使えるもの、使えないものとがあるので、ドキュメント Modules で確認する。
ドキュメントの記載は以下のように、ディレクティブ名、シンタックス、デフォルト動作、コンテキストの記載があり、その後にそのディレクティブの詳細説明が続く構成となっている。

 location
 
 syntax: location [=|~|~*|^~|@] /uri/ { ... }
 
 default: no
 
 context: server

インストール時のオプション、パッケージマネージャー等で多少の違いはあるが、インストール直後の $NGINX_ROOT ディレクトリ配下は以下の構成となっている。(include ディレクティブでは起点が nginx.conf の存在するディレクトリになる。後述。)

$ ll
total 42
drwx------  2 nginx root 4096 2012-03-04 15:21 client_body_temp
drwxr-xr-x  4 root  root 4096 2012-03-10 14:46 conf
drwx------  2 nginx root 4096 2012-03-04 15:21 fastcgi_temp
drwxr-xr-x  2 root  root 4096 2012-03-04 15:14 html
drwxr-xr-x  2 nginx root 4096 2012-03-10 17:22 logs
drwx------  5 nginx root 4096 2012-03-05 22:30 proxy_temp
drwxr-xr-x  2 root  root 4096 2012-03-04 15:14 sbin
drwx------  2 nginx root 4096 2012-03-04 15:21 scgi_temp
drwx------  2 nginx root 4096 2012-03-04 15:21 uwsgi_temp

設定内でのパスの指定には、絶対パスと相対パスが使えるが、相対パスを使った場合の起点は、$NGINX_ROOT となる。

デフォルトの設定ファイルは、 $NGINX_ROOT/conf/nginx.conf.default として残されており、このファイルを読みながら、Nginx の設定の概要を掴むことにする。

グローバルな設定

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

上記の記載で nginx.conf.default は始まっている。

  • # で始まる行はコメントなので解釈されない。
  • ディレクティブは変数(例: worker_processes, pid)で始まり、その引数(1, logs/nginx.pid)、または、連続した引数(logs/error.log notice)を含む。
  • 全てのディレクティブは ; (セミコロン)で終える。
  • ディレクティブにはサブディレクティブを含む(ネストする)ことができる。(上記の events のように。)
    これらのサブディレクティブは { } を使って、コンテキストとスコープを定義する。
  • スペースは無視されるので、インデントをうまく使って可読性を上げることができる。
  • Nginx は、1つのマスタープロセスと実際にリクエストを処理する複数のワーカープロセスで動作する。ワーカープロセスは非特権ユーザで動作するが、user ディレクティブにはそのユーザを指定できる。worker_processes には、動作させるワーカープロセスのプロセス数を指定する。

さらに読み進める。

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

http ディレクティブのブロック({ })が書かれている。

  • include ディレクティブは mime.types ファイルをインクルードしている。相対パスでの記述になっており、この設定ファイルと同じディレクトリに mime.types ファイルが存在する。通常、設定ファイルに記載される相対パスは、$NGINX_ROOT が起点となるが、include ディレクティブの場合は、nginx.conf ファイルの存在するディレクトリが起点となる。(Since version 0.6.7 CoreModule::include)
    このように設定ファイルを外出して include ステートメントで読み込むことができるので、設定ファイルの可動性はあがり、管理し易くなっている。
  • log_format をコメントアウトし、要件に合わせたログフォーマットに変更することができる。
  • gzip ディレクティブはラストマイル環境がよくない場合など効果を発揮するコンテンツの圧縮をオンザフライで実行する。Apache でいうところの mod_deflate の機能。細かな設定が行えるので、詳細は HttpGzipModule を参照のこと。

access_log ディレクティブの設定サンプルを見てみる。

access_log logs/example.access.log;
access_log /srv/http/example.org/logs/access.log;
access_log /var/log/nginx/access/example.org;
access_log off;
  • 最初の例では、相対パスになっているので、この場合は、ログは$NGINX_ROOT/logs/example.access.logに記録される。
  • 続く2つ目、3つ目の例では、access.log へのフルパスでの指定を行っている。
  • ログを残したく無い場合、4つ目の例のようにオフにすることも可能。(あまりやらないだろうが・・・)

サーバの設定 - server ディレクティブ

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

上記の server ディレクティブは、http ディレクティブに内包されている(http ディレクティブのコンテキストにある)。

server ディレクティブのブロックはそれぞれのサーバ自体の設定を行うところとなる。

listen ポートの設定 - listen ディレクティブ

まず listen ディレクティブだが、上記は 80 番ポートで待機していることを意味する。更に柔軟な記載が行えるようになっているので、詳細は、HttpCoreModule::listen を参照のこと。

バーチャルホストの設定 - server_name ディレクティブ

server_name ディレクティブは、名前ベースのバーチャルホストを実現する。
1つの IP アドレスで待機しているサーバは、リクエストヘッダーの HOST 名に基づいて、複数のドメインのホスティングを行える。

# サンプル1
server_name   example.com;
# サンプル2
server_name   example.com www.example.com;
# サンプル3
server_name   *.example.com;
# サンプル4
server_name   .example.com;
# サンプル5
server_name   example.*;
# サンプル6
server_name   example.com exmaple.net example.org;
# サンプル7
server_name   localhost example1 example2;
# サンプル8
server_name   "";

上記サンプルは、Basic Nginx Configuration – Linode Library の例から使わせてもらっている。

個々の名前は、スペース区切りで記述できる。Nginx はサーバに対して1つ以上の名前を持たせることができ、特定の server_name ディレクティブは1つ以上の名前のリクエストに応答する。また、ワイルドカードの定義や、正規表現での定義も行える。

  • 最初のサンプルは、server_nameexample.com を定義している。これにより、http://example.com にはこのサーバが応答する。
  • 2つ目の例の example.com www.example.com の場合、http://example.comhttp://www.example.com に応答する。
  • *.example.com.example.com の記述の意味は等しい。また、この記述の場合、サブドメインも全てハンドリングする。例えば、www.example.comimg.example.comsearch.example.com など。
  • 5つ目の例の example.* の記述は、example で始まる全てのドメインへのリクエストをハンドルする。 例えば、example.comexample.netexample.org など。example.wao.comexample.hello.com も同様。
  • 6つ目の example.com exmaple.net example.org は3つのドメイン全てへのリクエストをハンドルする。
  • Nginx は無効なドメイン名でもバーチャルホストとして定義できる。7つ目の例の localhost example1 example2 の場合、ホスト名となっているが、Nginx はリクエストの HTTP のヘッダのみを見てリクエストを処理するので、有効なドメイン名である必要はない。ただし、この場合は、hosts ファイルにホスト名が定義されている必要がある。
  • 最後の "" の定義では、Nginx は全てのリクエストを処理することを意味する。

リソース(ロケーション)の設定 - location ディレクティブ

次に location ディレクティブ。location ディレクティブは、クライアントによってリクエストされたロケーションに対する振舞いを定義する。

location ディレクティブは、そのブロックの前に適用するパターンを記載する。シンタックスは以下の通りとなっており、まずはこの動作概要を掴んでおくことが必要。

location [=|~|~*|^~|@] pattern { ... }

Basic Nginx Configuration – Linode Library の例を利用させていただき、その動作を理解する。

# パターングループ1
location / { }
location /images/ { }
location /blog/ { }
location /planet/ { }
location /planet/blog/ { }

# パターングループ2
location ~ IndexPage\.php$ { }
location ~ ^/BlogPlanet(/|/index\.php)$ { }

# パターングループ3
location ~* \.(pl|cgi|perl|prl)$ { }
location ~* \.(md|mdwn|txt|mkdn)$ { }

# パターングループ4
location ^~ /images/IndexPage/ { }
location ^~ /blog/BlogPlanet/ { }

# パターングループ5
location = / { }
  • 最初の5つのサンプルはリテラル文字列マッチと言われるもの。ホスト名に続くリクエストの最初のパートにマッチする。
  • server_nameexample.com が設定されており、http://example.com/ へのリクエストがあったとする。この時、”location /” ディレクティブはこのリクエストにマッチする。Nginx は “most specific match” でリクエストの条件を満たす。http://example.com/planet/blog/http://example.com/planet/blog/about へのリクエストは、”location /planet/blog/” で満たされる。当然、”location /planet/” でも満たされることになる。
  • 2つ目のグループの ~ (チルダ)に続くサンプルでは、Nginx は正規表現での適用を行う。これらの ~ だけのマッチ表現は case-sensive (大文字小文字を区別する) になる。最初の例では、IndexPage.php はマッチするが、indexpage.php はマッチしない。また、/BlogPlanet/blogplanet//blogplanet/index.php もマッチしない。Nginx は Perl Compatible Regular Expressions (PCRE) を使用している。
  • 3つ目のグループの ~* の表現は、~ の場合と同じく正規表現でのマッチで、case-insentive (大文字小文字を区別しない)になる。ここでの表現では、特定の拡張子で終わるファイル名に対してどう処理するのかを定義している。最初の例では、.pl、.PL、.cgi、.CGI、.perl、.Perl、.prl、.PrL、何れの拡張子のファイルにも適用される。
  • 4つ目のグループの ^~ の表現は、最初のグループのリテラル文字列マッチのように動作する。注意点として、このマッチ文が適用されると Nginx はパターンの検索を中止する。”^~ /images/IndexPage/“、”^~ /blog/BlogPlanet/” パターンでの location ディレクティブは、仮に他にマッチする location ディレクティブが他にあったとしても、このパターンがマッチした時点で使用される。
  • 最後の = の表現は、リクエストされたパスに完全一致した場合に適用され、マッチした時点で他のパターンの検索を中止する。例えば、最後のサンプルは、http://example.com/ にはマッチするが、http://example.com/index.html にはマッチしない。

ここまでの記述だけでは、location ディレクティブの全体の適用順がイマイチ掴みきれない。location ディレクティブのパターン適用ルールは以下の通りとなる。

  1. 完全一致(=)が最初に処理される。マッチするものが見つかった場合、Nginx はそこでパターンの検索を中止し、リクエストを実行する。
  2. 次に残りのリテラル文字列ディレクティブが処理される。もし、^~ が使用されていれば、Nginx はそこでパターンの検索を中止し、リクエストを実行する。それ以外の場合は、Nginx は location ディレクティブを処理し続ける。
  3. 正規表現で定義された(~~*)全ての location ディレクティブが処理される。正規表現がマッチしたら、Nginx はそこでパターンの検索を中止し、リクエストを実行する。
  4. 正規表現がない、または、正規表現がマッチしなかったときは、最も適切なリテラル文字列のパターンが使用される。

一旦 Nginx が特定のリクエストに対してリソースを提供する “location” を選択したら、このリクエストに対するレスポンスは、”location” ディレクティブのブロックによって定義される。

location / {
    root   html;
    index  index.html index.htm;
}

ドキュメントルートは、root ディレクティブによって、html ディレクトリに定義されている。上記は相対パスでの指定なので、実際のディレクトリは、$NGINX_ROOT/html となる。/blog/includes/style.css へのリクエストは、他の location ディレクティブがマッチしていないと想定した場合、$NGINX_ROOT/html/blog/includes/style.css にあるファイルがリソースの対象となる。root ディレクティブには絶対パスを指定することもできる。

index ディレクティブはリクエストがファイル名を含んでいない場合に、ファイルシステムのどのファイルが使用されるべきかを指定する。http://example.com/ へのリクエストは、$NGINX_ROOT/http/index.html のファイルがリソースの対象となる。複数のファイル名指定されている場合、Nginx は順にそのリストを処理し、存在するファイルにリクエストを適用する。上記の例の場合、index.html が存在しなければ、index.htm が使用される。仮にどちらも存在しない場合には、404 メッセーが送出される。

設定ファイルの管理

include ディレクティブを活用し、ベースとなる $NGINX_ROOT/conf/nginx.conf には必要最低限の記述のみを行い、各サーバ毎の固有の設定は別ファイルに切り出しておくとメンテナンス性があがる。

例えば、以下のような書き方。$NGINX_ROOT/conf/nginx.conf の内容は至ってシンプルになる。

user nginx;
worker_processes 4;
pid /var/run/nginx.pid;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

最後の include ディレクティブによって、個々のバーチャルホスト毎の設定は別ファイルに定義を行っている。

参考情報

マスターの導きを受けつつ、TDD で Ruby を学びながら悟りを開く - Koans

Written by @dr_taka_n at 2011/08/14 23:55 []

Koans とは

クイズ形式で Ruby を学ぶ Koans。その手法がなかなか凝っている。

koans

The goal is to learn the Ruby language, syntax, structure, and some common functions and libraries. We also teach you culture. Testing is not just something we pay lip service to, but something we live. It is essential in your quest to learn and do great things in the language.

Koans は Ruby 言語の文法、構造、そして、幾つかの共通関数やライブラリを学ぶことをゴールとしたもので、TDD の手法を用いてそれを実現している。

path_to_enlightenment.rb というファイルを起動することで、あるテストケースがカルマにダメージを与えたとのメッセージが表示され、マスターから助言を授かることになる。
表示されるメッセージを頼りにテストケースをパスするように修正していくことで、次の新たな設問(新たなカルマへのダメージ)へと進み、30ちょっとのカテゴリの計272問(2010/12/23版)の設問をクリアすることで悟りをひらけることになる。

設問をクリアしていく過程は以下のようになる。
例えば2番目の設問は以下の通りとなっている。

koans2

マスターが「君はまだ悟りをひらいていない」と言っている。”The answers you seek…” ということで “This shoud be true – Please fix this” とある。
「please meditate on the following code:」ということで、about_asserts.rb:16:in test_assert_with_message’` が指定されているので、該当ファイルの該当行を確認すると、

  # Enlightenment may be more easily achieved with appropriate
  # messages.
  def test_assert_with_message
    assert false, "This should be true -- Please fix this"  # line: 16
  end

assert が false となっている。当然これではテストは通らないので、true に書き変えて保存する。

再度 path_to_enlightenment.rb を起動してあげると、

$ ruby path_to_enlightenment.rb 
AboutAsserts#test_assert_truth has expanded your awareness.
AboutAsserts#test_assert_with_message has expanded your awareness.
AboutAsserts#test_assert_equality has damaged your karma.

The Master says:
  You have not yet reached enlightenment.
  You are progressing. Excellent. 2 completed.

The answers you seek...
  Failed assertion, no message given.

Please meditate on the following code:
  /home/hoge/Dropbox/work/koans/about_asserts.rb:25:in `test_assert_equality'

learn the rules so you know how to break them properly
your path thus far [.X________________________________________________] 2/274

次の設問へと続く。

基本は、テストケースのアサーションの期待値を埋めていくことで設問に答えていくことになる。

メッセージ(“The ansers you seek…“)に既に答えが出てしまってはいるが、そこはじっくり読まずに、テストケースとテストメソッド名の方に集中して何を学ばせようとしているのか頭の片隅に置きながら期待値を考えていると、よく考えられた設問になっているなぁと感心してしまう。

最初の方こそ上記のようなホントに初歩的な設問だが、徐々に内容が濃くなってくるので、なかなか良い復習、勉強になる。

また、一気に 274 問実施するのは流石にキツイので、できる時間に少しづつ実施することが可能な作りになっているのもうれしい。何問目まで回答できているかは .path_progress というファイルが覚えておいてくれているので、特に意識することなく、path_to_enlightenment.rb を起動すれば前回の設問から続きを行うことができる。

watchr を使って回答に集中

設問に答えては path_to_enlightenment.rb を起動するというのはメンドイ。
自動化しておく。

watchr gem を使うとファイルを監視下におき、編集された際に実行させたいことを定義することができる。 回答を記述するファイルを編集するたびに自動的に path_to_enlightenment.rb が実行されるようにしておく。

watchr をインストールしていない場合はインストールしておく。

$ sudo gem install watchr

path_to_enlightenment.rb と同じディレクトリに以下のファイルを用意しておく。

koans.watchr:

def run
  system("clear; ruby path_to_enlightenment.rb")
end

watch('.*\.rb$') { run }
run

watchr を起動しておく。

$ watchr koans.watchr

これで回答を行うファイルを編集する度にコンソールがリフレッシュされ path_to_enlightenment.rb の結果を確認することができる。

triangle.rb

大半は assertion の期待値を埋めていくのだが、テストケースを通す為の関数を書く設問もある。

以下の2問。

  • 設問その1:about_triangle_project.rb
class AboutTriangleProject < EdgeCase::Koan
  def test_equilateral_triangles_have_equal_sides
    assert_equal :equilateral, triangle(2, 2, 2)
    assert_equal :equilateral, triangle(10, 10, 10)
  end

  def test_isosceles_triangles_have_exactly_two_sides_equal
    assert_equal :isosceles, triangle(3, 4, 4)
    assert_equal :isosceles, triangle(4, 3, 4)
    assert_equal :isosceles, triangle(4, 4, 3)
    assert_equal :isosceles, triangle(10, 10, 2)
  end

  def test_scalene_triangles_have_no_equal_sides
    assert_equal :scalene, triangle(3, 4, 5)
    assert_equal :scalene, triangle(10, 11, 12)
    assert_equal :scalene, triangle(5, 4, 2)
  end
end
  • 設問その2:about_triangle_project_2.rb
class AboutTriangleProject2 < EdgeCase::Koan
  # The first assignment did not talk about how to handle errors.
  # Let's handle that part now.
  def test_illegal_triangles_throw_exceptions
    assert_raise(TriangleError) do triangle(0, 0, 0) end
    assert_raise(TriangleError) do triangle(3, 4, -5) end
    assert_raise(TriangleError) do triangle(1, 1, 3) end
    assert_raise(TriangleError) do triangle(2, 4, 2) end
 end
end

設問その1のテストの要件を満たす関数を用意し、かつ、設問その2の制約もパスする必要がある。

答えは提供されていない(見つけられなかった。。)ので、ベストアンサーかどうかはわからないが、以下の関数を書くことでテストをパスしている。
ツッコミありましたらご指摘願いたい。

triangle.rb:

def triangle(a, b, c)
  raise TriangleError.new if [a, b, c].any? { |e| e <= 0 }
  raise TriangleError.new if ((a + b) <= c) || ((a + c) <= b) || ((b + c ) <= a)

  case [a, b, c].uniq.size
    when 1 then :equilateral
    when 2 then :isosceles
    else :scalene
  end
end