CentOS7にphpenv/php-buildを入れてPHPのバージョンを自在に切り替える

前提

以下の環境で動作確認

要素 バージョン
CentOS 7.3.1611
git 1.8.3.1
phpenv 1.1.1-2

概要

Dockerで構築したほぼ空っぽのCentOS7に対し、各種パッケージをインストール後、phpenv/php-buildをインストールし、PHPのバージョンを自在に切り替えたお話。PHPのバージョンを上げたり下げたりすることを容易に行うことが目的。

本記事ではほぼ空っぽのCentOS7を利用しているので、色々な基本となるパッケージの導入も行ってるが、多くの環境では既に入っているものが殆どなので必要に応じてインストールする形で問題ない。

phpenv/php-buildとは

phpenv: PHPの複数のバージョンを一元管理し、バージョンの切り替えを容易に行うためのツール
php-build: phpenvで使用するPHPのインストールツール

下準備

ツールはGithubで公開されてるのでgitが必要。ファイルの書き換えなどでvimを使う。パッケージのダウンロードにwgetを使う。

$ yum install -y git vim wget

CentOS7の標準リポジトリだけだと足りないので、EPELを追加する。

$ yum install epel-release

EPELリポジトリを有効にする(デフォルトで有効になってる?)

$ vim /etc/yum.repos.d/epel.repo
enabled=1

PHPに依存するパッケージ、コンパイルに必要パッケージなどなどを纏めてドン(元々入ってるものが多数、不要なものもあるかも)
PHPのバージョンによっては依存モジュールが異なる場合がある模様。その都度エラーメッセージが出るのでインストールすればよい。

$ yum install -y yum install gcc bison libxml2 libxml2-devel openssl-devel libcurl-devel libjpeg-turbo-devel libpng-devel libmcrypt-devel readline-devel libtidy-devel libxslt-devel bzip2 bzip2-devel libicu-devel  gcc-c++ tidyp make re2c file libtool-ltdl-devel autoconf automake patch mysql-devel

phpenvのインストール

Githubから本体を落とす。/usr/srcあたりに置くのが定石なのでそこでclone

$ cd /usr/src
$ git clone https://github.com/CHH/phpenv.git

本体の中にインストールスクリプトが含まれているのでそれを実行する

$ cd phpenv/bin/
$ ./phpenv-install.sh
Installing phpenv in /root/.phpenv
remote: Counting objects: 2620, done.
remote: Total 2620 (delta 0), reused 0 (delta 0), pack-reused 2620
Receiving objects: 100% (2620/2620), 491.71 KiB | 368.00 KiB/s, done.
Resolving deltas: 100% (1640/1640), done.
Success.

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

Add above line at the end of your ~/.bashrc and restart your shell to use phpenv.

これでphpenvは使えるようになったが、コマンドですぐに利用できるようにパスを通す。
以下をbashrcなりに追記する

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

bashrcを再読込

$ source ~/.bashrc

phpenvが読めることを確認

$ phpenv --version
rbenv 1.1.1-2-g615f844

php-buildのインストール

PHPをインストールするためのphp-buildを取得し、phpenvのプラグインディレクトリに配置する。こちらもGithubから取得できるので以下の手順でCone

$ git clone https://github.com/CHH/php-build.git ~/.phpenv/plugins/php-build

以下コマンドでインストール可能なPHPの一覧が出てくるので、これが出ればOK

$ phpenv install --list
(中略)
  7.1.0
  7.1.1
  7.1.2
  7.1.3
  7.1.4
  7.1.5
  7.1.6
  7.1.7
  7.1.8
  7.1snapshot
  7.2.0beta1
  7.2.0beta2
  7.2snapshot
  master

最新のPHPをインストールする

前項でインストール可能なPHPの一覧を確認した所、開発段階の7.2までインストール可能になっているが、今回は安定版の最新版である7.1.8をインストールする。

インストール時間が長い。その上インストールログがあんまり出ないので固まったとも思ってしまうが、気長に待てば終わるか、エラーメッセージがちゃんと出るので待機する。(本環境では11分程度)

$ phpenv install 7.1.8
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.1.8 into /root/.phpenv/versions/7.1.8
[Downloading]: https://secure.php.net/distributions/php-7.1.8.tar.bz2
[Preparing]: /tmp/php-build/source/7.1.8
[Compiling]: /tmp/php-build/source/7.1.8
[xdebug]: Installing version 2.5.5
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.5.5
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/7.1.8/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: Enabling Opcache...
[Info]: Done
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.7.1.8.20170815015550.log
[Success]: Built 7.1.8 successfully.

PHP7.1.8がインストールできたので、この環境で利用するデフォルトのPHPを7.1.8に設定する

$ phpenv global 7.1.8

PHP7.1.8が利用できる状態になったことが確認できる

$ php -v
PHP 7.1.8 (cli) (built: Aug 15 2017 02:06:59) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.8, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans

古いPHPをインストールし、切り替える

開発内容によっては意図的に古いPHPを使用せざるを得ない場面もある。そんな場合にphpenvは効果的。ここではPHP5.2.17(デフォルトでインストールできる最も古いPHP)をインストールする。

$ phpenv install 5.2.17
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 5.2.17 into /root/.phpenv/versions/5.2.17
[Downloading]: https://git.php.net/?p=web/php-distributions.git;a=blob;f=php-5.2.17.tar.bz2;h=90d654f7b9c60320f6228194ed1d2f12976a3a59;hb=bed93a30bbc2104f6e88e6ef142891a4c289502f
[Info]: Applying patches: /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/gmp.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/xp_ssl.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/zip_direct.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/php-5.4.6-libxml2-2.9.patch
[Preparing]: /tmp/php-build/source/5.2.17
[Compiling]: /tmp/php-build/source/5.2.17
[xdebug]: Installing version 2.2.7
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.2.7
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/5.2.17/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.5.2.17.20170815021852.log
[Success]: Built 5.2.17 successfully.

versionsコマンドを叩くと、以下のようにインストール済みのPHP一覧と、デフォルトに設定されたバージョン(7.1.8)が確認できる

$ phpenv versions
  5.2.17
* 7.1.8 (set by /root/.phpenv/version)

globalコマンドでバージョンを切り替えると

$ phpenv global 5.2.17

デフォルトが変わり

$ phpenv versions
* 5.2.17 (set by /root/.phpenv/version)
  7.1.8

5.2.17が利用できる状態になる

$ php --version
PHP 5.2.17 (cli) (built: Aug 15 2017 02:23:16)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.2.7, Copyright (c) 2002-2015, by Derick Rethans

備考

phpenv installで、以下のようなエラーが出て

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

何らかの対応を行った後は、中途半端にデータが残っている可能性のある下記ディレクトリを削除しておいたほうが無難な模様

$ rm /tmp/php-build -rf

所感

  • これまでrbenv(ruby),pyenv(python),nodenv(node)を使ってきたが、それ比べて導入に一番苦労した。依存が多すぎる。
  • 同様に一番インストールにも時間がかかる。コンパイルが長すぎる
  • いくつかのバージョンを一度入れてしまえば後は他の言語と同様なので容易
  • apacheとか関連モジュールは結局バージョンに依存してしまうからあんまり効果的でないのかも
  • 他言語含めて、anyenvというのを使えばまとめて出来たりするらしい。今度調べたい。

Python3.6.2をインストールして、Wikipediaをクロールするスクリプトを書く

前提

本記事は以下の環境で動作確認済み

要素 バージョン
Debian 9.1
pyenv 1.1.3-7
python 3.6.2

概要

Pythonを使ったことのない私が、pyenvを用いて現在の最新版であるPython3.6.2をインストールし、Wikipediaの特定ページからランダムにリンクを辿り続けるスクリプトを書いたお話。

Python3.6.2のインストール

pyenvについて

pyenvは、複数のPythonのバージョンを容易にインストール、切り替えを行うツール。Rubyのrbenvや、nodeのnvm,nなどと同じようなもの。

pyenvのインストール

以下ページをそのまま参考にした。
PyenvによるPython3.x環境構築(CentOS, Ubuntu) – Qiita

3.6.2のインストール

前述のページだと若干古いPythonを使っているので、pyenvを用いて以下のように3.6.2をインストール

$ pyenv install -v 3.6.2
$ pyenv global 3.6.2

確認

$ python --version
Python 3.6.2

Python3入門

初Pythonだったのでまずは基本的な使い方を確認。以下のページが広く浅くしっかり丁寧だったので大変お世話になった。
Python3基礎文法 – Qiita

Wikipediaをクロールするスクリプト作成

概要

Pythonの基本を学んだので、手始めに、以下のような簡単なWebクロールスクリプトを書いてみる。

  • 特定のWikipediaのページを開始地点とする
  • 記事内の、他の単語へのリンク一覧を取得する
  • ノイズとなるリンクを排除し、関連する単語へのリンクのみを取り出す
  • 残ったリンク一覧から、ランダムに1件選出し、そのリンク先へ移動する
  • リンク先のページタイトルを標準出力し、同様の操作を繰り返す

Wikipediaを使った連想ゲームのような物?

使用ライブラリ

調べてみると、PythonにはWebクロールのためのフレームワーク/ライブラリも充実してるようだが、今回はPythonのお試しも含んでいるので、HTTPにrequests、HTML/XMLパーサにBeautifulSoupというライブラリを用いることにする。

実装

そんなに長くないので先に全文

BASEURL = 'https://ja.wikipedia.org'

# 指定したWikipediaページ内のaタグからランダムで一つ戻す
def get_wiki_a_tag(url):
  response = requests.get(url)
  soup = BeautifulSoup(response.text, 'lxml')
  print(soup.find('h1').text)
  a_tags = filter(
    lambda a: 'href' in a.attrs and
               a.text and
               a.attrs['href'].startswith('/wiki') and
               a.attrs['href'].find('Wikipedia') and
               a.text.find('年') == -1,
    soup.select('.mw-parser-output p a')
  )
  a_tags_list = list(a_tags)
  if not len(a_tags_list):
    print("end")
    sys.exit()
  return random.sample(a_tags_list, 1)[0]

# 開始地点を設定
word  = 'Python'
url   = f"{BASEURL}/wiki/{word}"
# ランダムにWikipediaのリンクを飛び続ける
while True:
  a_tag = get_wiki_a_tag(url)
  url  = f"{BASEURL}{a_tag.attrs['href']}"
  time.sleep(1)

まず、以下の部分で特定URLのHTTPレスポンスを取得する。BeautifulSoupを用いることで、レスポンスのXML/HTMLをparseできるようにする。

response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')

関連する単語へのリンクは概ねdiv.mw-parser-output配下のp要素に含まれているので、その中のaタグの一覧を取得する。あくまで概ねなので正確ではない。

soup.select('.mw-parser-output p a')

ノイズとなるリンクはpythonのfilter関数で排除する。排除対象は以下の通り

  • href属性が設定されてないaタグ
  • テキストが設定されていないaタグ
  • リンク先が/wiki で始まらないaタグ
  • リンク先にWikipediaが含まれるaタグ(要出典、検証可能性など)
  • テキストに「年」が含まれるaタグ(‘2010年’などの記事が頻出するため)
lambda a: 'href' in a.attrs and
           a.text and
           a.attrs['href'].startswith('/wiki') and
           a.attrs['href'].find('Wikipedia') and
           a.text.find('年') == -1,

randomパッケージのsample関数を用いて、aタグの一覧からランダムに一つ戻す。RubyだとArrayにsampleメソッドがあるのでちょっと不便だと思った。

return random.sample(a_tags_list, 1)[0]

動作確認

Pythonスタートでやってみたところ、初めのうちは良い感じだったが、トレインチャンネルあたりからおかしくなった。

もう一度。Googleから何故サウナ風呂につながるのか‥‥。
※確認した所、社内にサウナがあるらしい

試しに開始地点を阿部寛にしたらやたらとグローバリゼーションになった。阿部寛だしそんなものか。

所感

  • とりあえず何かを作ることを通じてPythonに世界に飛び込んでみたが、まだまだ魅力は掴めてない。インデントブロックがPythonの特徴の一つだが、これもまだ見やすいとは思えない(インデント幅を4字にしたらまた違うかも)
  • Rubyに慣れてて、Rubyが一番好きな分、Pythonとはウマが合わない可能性もあるが、もっと使ってみて自分に合うとこを見つけていきたい。
  • 今回作ったスクリプト、ずっと眺めてられそう。

はじめてのvimscriptとvimプラグインの作成

前提

以下の環境で作業を行った

要素 バージョン
debian 8.6
vim 7.4
neobundle 7.2.051
git 2.11.0

概要

vimは長らく使ってきたけどvimscriptを書いたことがない私が、vimscriptの基本を勉強し、簡単なvimプラグインを作成、NeoBundleでインストールできる状態にしたお話。

Vim scriptとは

Vim上で動作するスクリプトのこと。

:set filetype=html

のような、コマンドモードで”:”から始まるコマンドの集合。

最も身近なvimscriptは.vimrcで、これはvim起動時に読み込まれるvimscriptである。
つまり、vimscriptの書き方を知っていれば、より効果的なvimrcを記述することができる。

Vim script入門

vim scriptの入門は、以下を参考にした。
Vim script と vimrc の正しい書き方

本記事は簡単に基本文法を紹介する程度にするので、具体的に学びたい場合は各種参考ページを利用すること。

実行方法

vimscriptは基本的にscript.vimのように、拡張子をvimにしてファイルを作成する。
vimファイルはvimコマンドから実行することもできるが、多分以下のように、スクリプトをvimで編集中にsourceコマンドを叩くのが一番早い。

変数と代入

vim scriptには変数宣言の概念はなく、変数への代入と同時に変数が作成される模様。
let で変数への代入を行う。echoを見てもわかるように、シェルスクリプトに似ていると感じる。

let number = 1
let string = "hoge"
let array  = [1,2,3,4,5]

echo number
echo string
echo array

実行結果

1
hoge
[1, 2, 3, 4, 5]

条件分岐

ifとelseifとendifを使う。これもシェルスクリプトと同じ。

let number = 1
if number == 1
  echo "number is 1"
elseif number == 2
  echo "number is 2"
else
  echo "other"
endif

繰り返し

for文とwhile文が使える。while文の中でletを用いて変数を再宣言しているように見えるが、vim scriptにおけるletは代入のコマンドなので代入する場合は必ず必要。

for i in [1,2,3]
  echo i
endfor

let n = 0
while n < 10
  echo n
  let n += 1
endwhile

関数

関数もシェルスクリプトと同じようだが、気をつけたい所が2点。

まず、function! と”!”をつけることで、同名の関数が既にロード済みの場合上書きすることができる。こうしないと、同じバッファ内で複数回同じスクリプトを読んだ時に、同名の関数が既に定義済みだよとエラーが出るので”!”を付ける必要がある。

次に、関数の引数であるnumberを参照するために”a:”を頭につけている。これは変数のスコープの指定を表し、aとつけることでnumberは関数内にあるnumberであることを表す。aの他に、グローバルスコープ用の接頭辞やスクリプトファイル内スコープ用の接頭辞があるので、必要に応じて書き換える必要がある。

function! Double(number)
  return a:number * 2
endfunction

let result = Double(10)
echo Double(10)

vimプラグインの作成

本記事では、簡単すぎず難しくもないシンプルなvimプラグインを実装する。

今回作成するプラグインは、:RandDateTimeと入力することで、1980年から、2017年までの範囲内のランダムな日付を、Y-M-D H:m:sの形式でカーソル位置に挿入するコマンドを利用できるようにするプラグインで、まったくもって実用性はないが、個人的にもう少し改良して実用的なものにする予定。

ディレクトリ構成

作成するプラグイン名をmy-vim-pluginとした場合、同名のディレクトリを作成し、その中にpluginというディレクトリを作成、さらにその下にvim scriptを記述する。my-vim-plugin直下にはREADMEを入れたりするみたいだが、今回はスクリプトファイル一つで完結させるので、ディレクトリ構成は下記のようになる。

vagrant$ tree my-vim-plugin/
my-vim-plugin/
└── plugin
    └── script.vim

1 directory, 1 file

乱数を生成する関数

まず、ランダムな日付を生成するために、指定した範囲内での数値をランダムに戻す関数を実装する。
残念ながら、vim scriptには乱数を生成するための仕組みは存在しない。とは言え、今回は時刻データを用いて擬似的な乱数を生成することにした。

作成した関数は以下の通り。GetRandom関数は、minとmaxを引数にとり、min <= n <= max の範囲のランダムな整数を戻す。
reltime関数は、現在のUNIXTIMEを秒とマイクロ秒の配列で戻す。配列の二番目の要素がマイクロ秒になるので、その数値を使って擬似乱数を生成している。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

ランダムな日付を取得する関数

前項で作成したGetRandom関数を用いて、1980年から2017年までのランダムな日付文字列を戻す、GetRandomDate関数を以下のように実装する。なお、今回は実装の簡略化のため、日は1日から28日までの確実に存在する日付のみを対象とするようにした。

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

ランダムな日付時刻を取得する関数

同様に、前項で作成したGetRandomDate関数を用いて、ランダムな時刻までを含めて戻すGetRandomDateTime関数を以下のように実装する

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

ランダムな日付時刻をカーソル位置に挿入する関数

前項で作成したGetRandomDateTime関数は、ランダム日付時刻を取得するだけなので、新たにInsertRandomDateTime関数を以下のように実装する。
executeコマンドは、以降の任意のコマンドをvim上で実行するためのコマンド。:normalは以降のコマンドをノーマルモードで実行するコマンド。aでカーソルの次の位置から挿入モードを開始、そしてdatetimeを入力するという手はずになる。

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

作成した関数を呼び出すコマンドを定義

ここまででランダム日付時刻文字列を挿入する仕組みができあがったので、それを呼び出すためのコマンドを定義する。コマンドの定義にはcomman関数を用い、コマンド名と実行内容を引数に設定する。(callは、指定した関数を実行するコマンド)

command! RandDateTime call InsertRandomDateTime()

動作確認

以上で、以下のような自作のvimプラグインが完成した。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

command! RandDateTime call InsertRandomDateTime()

これをsourceコマンドで読み込むことで、現在開いているバッファ上でRandDateTimeコマンドが利用できるようになる。

NeoBundleでインストールできるようにする

NeoBundleはvimプラグインを管理するためのツールで、決まったディレクトリ構成でGithub上にVimプラグインを公開すれば、誰でも手軽にそれをインストールできるようになる。

今回は既にNeoBundleを想定したディレクトリ構成にしているので、これをこのままGithub上にあげる。
Sa2Knight/my-vim-plugin

この状態で、既にNeoBundleが利用できる状態のvimrcに以下を追加

NeoBundle "Sa2Knight/my-vim-plugin"

vimを起動すると、以下のようにインストールするかの確認が出るので、Yを入力する

Not installed bundles:  ['my-vim-plugin']
Press ENTER or type command to continue
Install bundles now?
(y)es, [N]o:

Github経由で先程作成した自作プラグインがインストールされ、次回起動時から自動で:RandDateTimeコマンドが使えるようになる。

所感

vimscript及びvimプラグインの非常に基礎的な部分にようやく手を付けられたので、今後もっと深い部分を勉強して実用的なプラグインを作りたい。とりあえず今回作ったプラグインには以下のような改良点があるので、それを実装しつつノウハウを見つけていく。

  • ランダムの範囲を外から指定できるようにする
  • 日付時刻文字列のフォーマットを外から指定できるようにする
  • 乱数値が一桁になった場合0で詰めるようにする
  • コマンドを叩かずともキーバインドですぐに実行できるようにする

HTMLスケッチでHTML/CSSトレーニング(Backlog編)

HTMLスケッチとは

本記事で言う「HTML」スケッチは、概ね以下の行為を指すものとする

  • 実在するWebページをHTMLとCSSを使って自らの手で再現する
  • 見本となるWebページは単一のページとし、それのソースコードを参照せずに、再現する
  • 再現に使用する手法/技術は、見本に合わせる必要はなく、自分の持っている知識から試行錯誤して再現する

参考 プロデザイナーが実践するHTML/CSSスキルアップ術

HTMLスケッチを行うメリット

  • HTML/CSSのスキル底上げには実際にモノを作るのが一番だが、何を作ったら良いかわからない時に手っ取り早い
  • 公開されているWebページがどのような構造で実装されているかが見えてくるので、Webデザイン力の底上げにもなる
  • 自分でWebデザインした場合には使わないようなレイアウト/スタイルを使うことも出てくるので、技術の幅が広がる

フロントエンジニアがWebデザインスキルを身につける必要はあるか?

これについては色々な意見が飛び交ってるが、個人的にはモダンなフロントエンジニアになるためにはWebデザインスキルも必須だと思う。

最近はリッチクライアント(死語?)が当たり前になってきたため、クライアント側でも各種ライブラリ/フレームワークの導入が一般的になってきてるが、その中でもReactやVue(Component)など、ビューとそのロジックが密になったライブラリ/フレームワークを使う場面も多くなって来ている。ビューとロジックが密ということは、それだけエンジニアとデザイナの作業範囲を分離しづらいということだ。

そうなると、デザインの諸々をデザイナに投げて放ったらかすというもの難しくなってくるため、エンジニアが最低限のデザインを施す必要がある。

よって、特にフロントエンジニアにもWebデザインスキルは必須になってくる(と思う)ので、本記事ではデザイナでなくフロントエンジニアの視点でHTMLスケッチを行う

HTMLスケッチのルール

以下のルールは本記事でのルールなので、実践する場合は各自やりやすいようにルール作りをしたほうが良い

  • 見本は既存Webサービスの特定の1画面をスクリーンショットで収めた範囲とする
  • 基本的にはスクリーンショットの画面を再現し、見えない範囲(オンマウスで見た目が変わるなど)については対応しない
  • JavaScriptが影響する部分は対応しない
  • bootstrapなどのCSSフレームワークの利用を禁止
  • 見本のソースコードを閲覧することは原則禁止する(スケッチ後に答え合わせとして確認する)
  • Saasを使う。今時生のCSSを直接書くのもナンセンスなので。と言ってもネスト構造ぐらいしか使う予定はない
  • 画像ファイルは見本のページから直接ダウンロードする(学習用なので見逃してください)
  • アイコンファイルは、今回はfontawesomeを用いて見本に近いアイコンを利用する
  • ブラウザ対応はそこまで拘らず、とりあえず見本撮影時に用いたChromeのみで検証する

今回の見本

例によってBacklogが好きなのでBacklogの課題一覧画面を対象とする。初めてのHTMLスケッチなので比較的簡単そうで、かつ身近な普段から見慣れている画面を見本とすることにした。

以下が今回見本とするスクリーンショット

作業環境の構築

以下の環境で作業を行う

要素 バージョン
Debian 8.6
sass 3.5.1
Compass 1.0.3

sassのインストール

sassはcssのプリプロセッサで、cssの弱い部分などを改良したメタ言語で書かれたファイルをcssに変換する。
簡単に言えばcssを効率的に記述するために必要なもの。

sassはgemライブラリで公開されてるので、rubyとgemが必要。本記事では割愛するので必要に応じてそれらもインストールすること。

vagrant$ sudo gem install sass

Compassのインストール

Compassは、sassで記述されたファイルの変更を検知し、自動でsassを実行し、変換後のcssファイルを生成してくれるツール。これが無いとファイルを変更するたびに変換コマンドを叩く必要があるので実質必須。

こちらもgemを用いてインストールする

vagrant$ sudo gem install compass

動作確認

compass createで作業環境をまとめて作成できる。

vagrant$ compass create
directory sass/
directory stylesheets/
   create config.rb
   create sass/screen.scss
   create sass/print.scss
   create sass/ie.scss
    write stylesheets/ie.css
    write stylesheets/print.css
    write stylesheets/screen.css
(以下略)

が、今回はscssファイル1個に全てまとめるつもりなので、生成されたsassファイルは消しておく

vagrant$ rm sass/*

compassを実行。watchコマンドで常駐化して変更を検知してくれる

vagrant$ compass watch
Compass is watching for changes. Press Ctrl-C to Stop.

この状態でsassディレクトリいかに、sassファイル(style.sass)を作成する

body {
  .hoge {
    font-size: 12px;
  }
  .fuga {
    font-size: 14px;
  }
}

するとcompassがそれを検知して

Compass is watching for changes. Press Ctrl-C to Stop.
  created sass/style.scss
    write stylesheets/style.css

自動でstyleseets/style.cssを生成してくれる

/* line 2, ../sass/style.scss */
body .hoge {
  font-size: 12px;
}
/* line 5, ../sass/style.scss */
body .fuga {
  font-size: 14px;
}

あとはこの生成されたcssファイルを参照するHTMLファイルを用意すれば準備完了

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="stylesheets/style.css">
  </head>
  <body>
  </body>
</html>

HTMLスケッチの実施

大枠の実装

まずはページ全体の大枠をマークアップすることに。見本から見える必要な構造を想像してHTMLに書き出す(コードが長いのでGithub参照)

ある程度全体構造を意識しながら主にdivで囲って、適切なclassを振ってCSSの適用に備えている。

まだスタイルがまったく適用されていないので、そのまま画面に表示されるだけ。色々足りてない構造もあるが、それは随時追加していくものとする。
ここからスタイルの適用と、それに応じてHTMLの書き換えも行っていく。

カラム割

ページ全体を以下の3カラム構成にする

  • ヘッダー
  • サイドメニュー
  • コンテンツ

カラム割の流行りはflexboxらしいけどCSS弱者なので先ずはfloatを使ったカラム割の経験を積む。サイドメニューの背景には画像ファイルがあったのでBacklogから拝借する。コンテンツ内にもヘッダーがあるので、そこにはprojectと名付けて50px確保する。

body {
  margin:  0;
  padding: 0;
  border:  0;
}
.header {
  height: 50px;
  background-color: #ebf0f2;
}
.wrap {
  .sidebar {
    width: 200px;
    height: 100%;
    float: left;
    background-color: #3b9dbd;
    background-image: url('../images/bg_dot.png');
  }

  .content {
    margin-left: 200px;
    background-color: #f0f0f0;
    .project {
      height: 50px;
      background-color: white;
    }
  }
}

Github

HTMLも微修正して以下の感じに。少しだけ全体像が見えてくる。

サイドメニューの実装

ぱっと見一番簡単そうなのがサイドメニューなので安直にこちらから手を出す。HTMLは、メニュー項目をul/liで記述し、アイコンはfontawsomeを利用する。

      <div class="sidebar">
        <div class="burger"><i class="fa fa-bars fa-2x" aria-hidden="true"></i></div>
        <ul class="sidemenu">
          <li><i class="fa fa-home fa-lg" aria-hidden="true"></i><span>ホーム</span></li>
          <li><i class="fa fa-list fa-lg" aria-hidden="true"></i><span>課題</span></li>
          <li><i class="fa fa-plus fa-lg" aria-hidden="true"></i><span>課題の追加</span></li>
          <li><i class="fa fa-wikipedia-w fa-lg" aria-hidden="true"></i><span>Wiki</span></li>
          <li><i class="fa fa-files-o fa-lg" aria-hidden="true"></i><span>ファイル</span></li>
          <li><i class="fa fa-git fa-lg" aria-hidden="true"></i><span>Git</span></li>
          <li><i class="fa fa-cog fa-lg" aria-hidden="true"></i><span>プロジェクト設定</span></li>
        </ul>
      </div>
    .burger {
      text-align: right;
      .fa {
        padding: 10 20 10 0;
      }
    }
    ul.sidemenu {
      padding-left: 0;
      font-size: 95%;
      list-style-type: none;
      li {
        padding-left: 12px;
        margin: 20 0 20 0;
        .fa {
          width: 20px;
        }
        span {
          padding-left: 15px;
        }
      }
    }
  }

Github
まだ比較的まともにCSSも書けてそれっぽい画面が出来上がってる。

ヘッダーの実装

コンテンツは最後として、次にヘッダーに着手する。結果から言えばここが一番苦労した。CSSよくわからない。ユーザアイコン的な画像はBacklogから拝借。li要素を横並びに表示するテクニックを身に着けたのでさっそく使ってる。

    <div class="header">
      <img class="logo" src="images/logo.svg">
      <ul class="menus">
        <li>ダッシュボード</li>
        <li>プロジェクト</li>
        <li>最近みた課題</li>
        <li>最近見たWiki</li>
        <li>フィルタ</li>
      </ul>
      <ul class="icons">
        <li><i class="fa fa-ellipsis-h fa-lg" aria-hidden="true"></i></li>
        <li><i class="fa fa-plus fa-lg" aria-hidden="true"></i></li>
        <li><i class="fa fa-eye fa-lg" aria-hidden="true"></i></li>
        <li><i class="fa fa-bell fa-lg" aria-hidden="true"></i></li>
        <li><i class="fa fa-search fa-lg" aria-hidden="true"></i></li>
        <li class="bars"><i class="fa fa-bars fa-lg" aria-hidden="true"></i></li>
        <li>
          <img class="user-menu" src="images/user.gif">
          <i class="fa fa-angle-down" aria-hidden="true"></i>
        </li>
      </ul>
    </div>
.header {
  background-color: #ebf0f2;
  * {
    vertical-align: middle;
  }
  ul {
    padding: 0;
    margin: 10px 0 10px 0;
    list-style-type: none;
    display: inline-block;
    li {
      display: inline-block;
      margin-left: 12px;
    }
  }
  img {
    width: 30px;
    max-height: 100%;
  }
  img.logo {
    padding-left: 20px;
  }
  .menus {
    font-size: 95%;
    width: 70%;
  }
  .icons {
    width: 25%;
    color: gray;
    text-align: right;
    .bars {
      padding-left: 20px;
      border-left: 1px solid gray;
    }
  }
}

Github

課題一覧の実装

メインの部分。この辺から妥協が生まれる。細かい部分を合わせるのが難しくなってきて、ゴリ押し気味なスタイリングが増えてくる。
テーブルは普通にtableタグを用いて、列単位で装飾。聞くところによるとTableの内部要素(tr/td)にmarginやpaddingを設定するのはナンセンスらしいけど、それを避けて装飾するのが上手く行かなかったので妥協。

        <div class="issues">
          <table>
            <thead>
              <tr>
                <th>種別</th>
                <th>キー</th>
                <th>件名</th>
                <th>状態</th>
                <th>カテゴリー</th>
                <th>登録日</th>
                <th>更新日</th>
                <th>登録者</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td><div class="task mark">タスク</div></td>
                <td><a href="">DEV-240</a></td>
                <td>githubのコミットログを@nullでツイートする方法の検討</td>
                <td><div class="status mark">未対応</div></td>
                <td>開発関係</td>
                <td>2017/07/30</td>
                <td>2017/07/30</td>
                <td class="user">
                  <img class="user-menu" src="images/user.gif">
                  <span>sa2knight</span>
                </td>
              </tr>
             <tr>
                <td><div class="task mark">タスク</div></td>
                <td><a href="">DEV-237</a></td>
                <td>vimでもっと強力なコード補完を導入する</td>
                <td><div class="status mark">未対応</div></td>
                <td>開発関係</td>
                <td>2017/07/30</td>
                <td>2017/07/30</td>
                <td class="user">
                  <img class="user-menu" src="images/user.gif">
                  <span>sa2knight</span>
                </td>
              </tr>
             <tr>
                <td><div class="task mark">タスク</div></td>
                <td><a href="">DEV-235</a></td>
                <td>[でぐろぐ]元ツイートページから画像ファイルのパスを抜き出すロジック</td>
                <td><div class="status mark">未対応</div></td>
                <td>開発関係</td>
                <td>2017/07/30</td>
                <td>2017/07/30</td>
                <td class="user">
                  <img class="user-menu" src="images/user.gif">
                  <span>sa2knight</span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
    .issues {
      padding: 0 30 0 30;
      table {
        width: 100%;
        border: solid 1px #c2c2c2;
        border-radius:   4px;
        border-collapse: collapse;
        thead {
          background-color: white;
          color: #00759b;
          font-size: 85%;
          tr {
            border-bottom: 2px solid #ccc;
          }
          th {
            padding: 7px 0px 7px 0px;
          }
        }
        tbody {
          font-size: 80%;
          a {
            color: #00759b;
            text-decoration: none;
          }
          tr {
            border-collapse: separate;
          }
          tr:nth-child(even) {
            background-color: #f6f6f6;
          }
          tr:nth-child(odd) {
            background-color: white;
          }
          td {
            padding: 5px 15px 5px 15px;
          }
          td > .mark {
            text-align: center;
            color: white;
            padding: 0 10 0 10;
            border-radius: 20px;
          }
          td > .task {
            background-color: #b0be3c;
          }
          td > .status {
            background-color: #ed8077;
          }
          td.user {
            img {
              vertical-align: middle;
              width:  24px;
              height: 24px;
            }
          }
        }
      }
    }

Github
ゴリ押したけど見た目はそれっぽい

課題一覧周辺のUI実装

ページャみたいなモノとかボタンとかのスタイリング。この当たりから作業がとても地味でCSSも汚くなってきた。
コードの修正が分散してきたのでGithub参照

検索フォームの実装とその他修正

あとは検索フォームをスタイリングして全体の仕上げをして完成。
最終的な成果物はこちら

上が今回作成したページで、下が見本となる本家Backlog。比べてみると中国産のパチもんみたいにも見えるが、概ね同じように出来ているので個人的には満足。

所感

良かった点

  • スケッチの過程でスタイリングに関する知らなかった定石/手法を身につけることができた
  • 最後にBacklogのCSSを見て答え合わせした中で、知らなかった属性などを学ぶことができた
  • 自分で一からスタイリングする経験が少なかったので、そういった経験を得られた

失敗した点

  • flota,absolute,text-alignなど、現在はあまり使われないスタイリング手法に頼りすぎて、変更に弱いCSSになったと自負してる。もう少しモダンなスタイリング技術を学びたい
  • BacklogのようなWebサービスは、通常動的にページが生成されるので必然的にHTMLの可読性が低くなるので、参考にしづらかった。俳優のホームページなど、静的なHTMLページを見本にしたほうが勉強になると思う
  • ファイルを更新した後にブラウザも更新するのが面倒だった。ブラウザのオートリロードツールなどを導入すればもう少し楽だったかも

Capybara(Rspec)で指定したセレクタ要素が存在しないことを検証する

以前は以下を参考にして例外を発生させて検証してたが、

Capybaraで要素がないことをテストする方法 | 自転車で通勤しましょ♪ブログ http://319ring.net/blog/archives/2546/

以下で充分検証することができた。例外待ちしない分こちらのほうが高速。

def cant_find(selecter)
  expect(page.all(selecter).empty?).to eq true
end

[Laravel] Exceptionをcatchできない

LaravelのController内で以下のように例外を補足するコードを書いても例外を補足できなかった

try {
  hoge();
} catch (Exception $e) {
  var_dump($e);
  exit;
}

散々ハマってPHPの例外処理の仕様まで追ったが、結果的に以下の修正で解決した。

try {
  hoge();
} catch (\Exception $e) {
  var_dump($e);
  exit;
}

namespace内のExceptionを待ち受けるか、全体のExceptionを待ち受け取るかの違い。

[Laravel] 対話的にプログラムを実行する

概要

laravelのtinkerを用いて対話的にプログラムを実行する。tinkerはRuby on Railsで言うところのrails consoleで、Laravelを用いた環境に対するプログラムの発行を行うツールである

起動

artisanのあるディレクトリに移動し、以下のコマンドで起動できる

$ php artisan tinker
Psy Shell v0.8.6 (PHP 5.6.30-12~ubuntu16.04.1+deb.sury.org+1 — cli) by Justin Hileman

PHPプログラムの実行

tinkerを起動すると、対話的にプログラムを実行することができる

>>> 5 + 2
=> 7
>>> 2 ** 8
=> 256

当然PHPの標準ライブラリを利用することも可能

>>> strlen('hogehoge');
=> 8
>>> array_map(function($n) { return $n * 10; } , [2,4,6,8,10]);
=> [
     20,
     40,
     60,
     80,
     100,
   ]

複数行に跨ぐことも可能。書きづらいので基本的にワンライナーであるべきだとは思う。

>>> function sayHello($name) {
...   return "Hello, $name";
... }
>>> sayHello('sasaki');
=> "Hello, sasaki"

LaravelModelの操作

Userモデルが存在する時、tinkerを用いてUserの作成と取得を行う。

作成

>>> $user = new App\User();
=> App\User {#703}
>>> $user->email = 'hogehoge@fugafuga.com';
=> "hogehoge@fugafuga.com"
>>> $user->password = md5('hogehoge');
=> "329435e5e66be809a656af105f42401e"
>>> $user->save();
=> true

取得

>>> $user = new App\User();
=> App\User {#703}
>>> $user->first();
=> App\User {#714
     id: 1,
     email: "hogehoge@fugafuga.com",
     name: "",
     created_at: "2017-07-11 11:37:49",
     updated_at: "2017-07-11 11:37:49",
     deleted_at: null,
   }

所感

  • DBの内容確認したいけどSQL書くのが面倒という時にも使えそう
  • 実装したロジックの動作確認の戦力になりそう
  • Laravel関係なくPHPの簡単な動作確認なんかにも使えそう

[Ruby] 外部からWordPressの記事を取得する

前提

以下の環境で実装/動作確認済み

要素 バージョン
ruby 2.3.1
gem 2.6.12
bundle 1.15.1
rubypress 1.2.2

概要

  • Rubyを用いて外部のWordPressの記事一覧、詳細を取得する
  • 対象WordPressの有効なアカウント情報が必要
  • 記事の投稿、編集など基本的な全ての処理が可能だが、本記事では取得までに抑える

XML-RPCの有効化

本記事ではWordPressのXML-RPC機能を用いて外部から操作を行う。ただしこの機能はセキュリティの観点から無効になっている場合が多い。その場合はwp_config.phpの以下の行をコメントアウトすることで有効化出来る。サーバに入れない環境の場合は諦めるしか無い。

add_filter('xmlrpc_enabled', '__return_false');

rubypressの導入

RubyでXML-RPCを用いてWordPressを操作する時に最も手軽に利用できそうなgemライブラリであるrubypressを導入する。ここでは空のGemfileを作成するところから始める

source "https://rubygems.org"
gem "rubypress"

ディレクトリ内にインストール

$ bundle install --path vendor/bundle

WordPressクライアントオブジェクトの作成

Rubypressを用いてClientオブジェクトを作成する。ここでは対象WordPressのホスト名、ユーザ名、パスワードが必要になる。

require 'rubypress'
class WordPress

  @@hostname = 'qs.nndo.jp'
  @@username = 'sasaki'
  @@password = 'XXXXXXXXXXXXXXXX'

  def initialize
    @wp_client = Rubypress::Client.new(
      host: @@hostname,
      path: '/xmlrpc.php',
      username: @@username,
      password: @@password,
    )
  end

end

記事を取得する主なメソッドの使い方

post_idを指定して取得

@wp_client.getPost(post_id: 13)

出力。長いので一部のみ掲示

{"post_id"=>"13",
 "post_title"=>"サンプル記事3",
 "post_date"=>
 "post_status"=>"publish",
 "post_content"=>"サンプル記事3本文",
 "post_parent"=>"0",
 "post_mime_type"=>"",
 "post_format"=>"standard",
 "terms"=>[],
 "custom_fields"=>[]}

一部のフィールドのみ取得

タイトルと本文さえあれば良いという場合は以下のように

@wp_client.getPost(post_id: 13, fields: [:post_title, :post_content])
{"post_id"=>"13", "post_title"=>"サンプル記事3", "post_content"=>"サンプル記事3"}

全ての記事一覧を取得

出力が長くなってしまうので、タイトルのみ取り出すようにする

@wp_client.getPosts(fields: [:post_title])
[{"post_id"=>"18", "post_title"=>"サンプル記事1"},
 {"post_id"=>"20", "post_title"=>"サンプル記事2"},
 {"post_id"=>"22", "post_title"=>"サンプル記事3"}]

カスタム投稿タイプを指定して記事一覧を取得

例えば”news”というカスタム投稿タイプがあるとして、newsの一覧を取得する

@wp_client.getPosts(filter: {post_type: :news}, fields: [:post_title])
[{"post_id"=>"8", "post_title"=>"サンプルニュース1"},
 {"post_id"=>"10", "post_title"=>"サンプルニュース2"}]

参考

RPMを個別にダウンロードし、yumのローカルリポジトリを構築する

前提

以下の環境で動作確認

要素 バージョン
CentOS 6.9
yum 3.2.29
createrepo 0.9.9

目的

インターネットに接続できない環境でRPMパッケージをインストールするために、事前にインターネットに接続できる環境でyumリポジトリを構築する。リポジトリはDVDなどの外部メディアを用いてインターネットに接続できない環境内に配置することを想定する。

リポジトリで管理するパッケージのダウンロード

今回は動作確認用としてnginxのRPMパッケージを含んだローカルリポジトリを構築する。

まずはリポジトリになるディレクトリを作成

$ mkdir /root/MyRepo

次にRPMパッケージをダウンロードする。
yum installコマンドでは、–downloadonlyオプションを付与することで、パッケージのインストールは行わずにダウンロードのみを行うことができる。–downloaddirオプションでダウンロード先ディレクトリを指定できるので、今回は/root/MyRepoを指定する。

$ yum install -y --downloadonly --downloaddir=/root/MyRepo nginx

MyRepoディレクトリを見ると、依存パッケージも含めてnginxのパッケージがダウンロードできていることがわかる

$ ls /root/MyRepo/
nginx-1.10.2-1.el6.x86_64.rpm                 nginx-mod-http-image-filter-1.10.2-1.el6.x86_64.rpm  nginx-mod-mail-1.10.2-1.el6.x86_64.rpm
nginx-all-modules-1.10.2-1.el6.noarch.rpm     nginx-mod-http-perl-1.10.2-1.el6.x86_64.rpm          nginx-mod-stream-1.10.2-1.el6.x86_64.rpm
nginx-mod-http-geoip-1.10.2-1.el6.x86_64.rpm  nginx-mod-http-xslt-filter-1.10.2-1.el6.x86_64.rpm

ローカルリポジトリの構築

yumリポジトリの構築には、createrepoというツールを使う。こちらはyumでインストールできるので普通にインストール。

$ yum install -y createrepo

インストールできていることを確認

$ createrepo --version
createrepo 0.9.9

MyRepoディレクトリ内に、yumリポジトリを構築

$ createrepo /root/MyRepo/
Spawning worker 0 with 8 pkgs
Workers Finished
Gathering worker results

Saving Primary metadata
Saving file lists metadata
Saving other metadata
Generating sqlite DBs
Sqlite DBs complete

MyRepoディレクトリ内にrepodataというディレクトリが作成されていれば成功

$ ls /root/MyRepo/repodata/
692561d7eb657f2cc6bd22928db903b26b6b24e4f220d3d69f80bb10b2b8490f-primary.xml.gz
8ff28a60f7a205ef1d69a193e7560f72ce26f2877788a7e61e429cead8b2115f-primary.sqlite.bz2
9e33288bed1a69b3f0161cee7a3fb456f424f371eb04265bbe0e8934cff4b17a-filelists.sqlite.bz2
a83bc7ec7e90bef8452061c3e3987f4fa45343450ba466f13266b4bfe3025965-filelists.xml.gz
d2387c0a071800a022eef83703840761a0294e6dc09e91b9ce02f9a45f1688de-other.xml.gz
fa70a39d2111e3440f54c2deddcf5df9c6014295f7ddcf648baf1bffd9bd468d-other.sqlite.bz2
repomd.xml

リポジトリへの参照を追加

前項でローカルリポジトリを作成したが、そこにリポジトリがあることをyumに教えて上げる必要がある。
yumが参照するリポジトリ一覧は、/etc/yum.repos.d/にある

$ ls /etc/yum.repos.d/
CentOS-Base.repo       CentOS-Vault.repo      docker-main.repo   mysql-community-source.repo  remi-php70.repo  remi.repo
CentOS-Debuginfo.repo  CentOS-fasttrack.repo  epel-testing.repo  mysql-community.repo         remi-php71.repo
CentOS-Media.repo      centalt.repo           epel.repo          remi-php54.repo              remi-safe.repo

ここに今回作成したMyRepoの情報を追加する

$ vim /etc/yum.repos.d/MyRepo.repo

エディタで以下のようにリポジトリの設定を記述
nameはリポジトリ名を適当に、baseurlには構築したリポジトリのパスを、gpgcheckは、署名なしで利用できることを示す0を設定する。

[MyRepo]
name=MyRepo
baseurl=file:///root/MyRepo
gpgcheck=0

yumコマンドでリポジトリが認識されていることを確認。ダウンロードしたnginxも認識されていることがわかる

$ yum list all | grep MyRepo
GeoIP.x86_64                              1.6.5-1.el6                  @MyRepo
GeoIP-GeoLite-data.noarch                 2017.01-1.el6                @MyRepo
GeoIP-GeoLite-data-extra.noarch           2017.01-1.el6                @MyRepo
gd.x86_64                                 2.0.35-11.el6                @MyRepo
geoipupdate.x86_64                        2.2.1-2.el6                  @MyRepo
nginx-filesystem.noarch                   1.10.2-1.el6                 @MyRepo
nginx.x86_64                              1.10.2-1.el6                 MyRepo
nginx-all-modules.noarch                  1.10.2-1.el6                 MyRepo
nginx-mod-http-geoip.x86_64               1.10.2-1.el6                 MyRepo
nginx-mod-http-image-filter.x86_64        1.10.2-1.el6                 MyRepo
nginx-mod-http-perl.x86_64                1.10.2-1.el6                 MyRepo
nginx-mod-http-xslt-filter.x86_64         1.10.2-1.el6                 MyRepo
nginx-mod-mail.x86_64                     1.10.2-1.el6                 MyRepo
nginx-mod-stream.x86_64                   1.10.2-1.el6                 MyRepo

ローカルリポジトリを用いたインストール

最後に、構築したローカルリポジトリを用いて、nginxのインストールを行う。

nginxがまだインストールされていないことを確認

$ which nginx
/usr/bin/which: no nginx in (/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin)

MyRepoリポジトリを用いてnginxをインストール
通常通りinstallコマンドを使うが、–disablerepoで一度全てのリポジトリを無効にし、–enablerepoでMyRepoのみを有効にすることで、確実にMyRepoリポジトリからnginxをインストールする

$ yum install -y --disablerepo=\* --enablerepo=MyRepo nginx
(中略)
インストール:
  nginx.x86_64 0:1.10.2-1.el6

依存性関連をインストールしました:
  nginx-all-modules.noarch 0:1.10.2-1.el6        nginx-mod-http-geoip.x86_64 0:1.10.2-1.el6            nginx-mod-http-image-filter.x86_64 0:1.10.2-1.el6
  nginx-mod-http-perl.x86_64 0:1.10.2-1.el6      nginx-mod-http-xslt-filter.x86_64 0:1.10.2-1.el6      nginx-mod-mail.x86_64 0:1.10.2-1.el6
  nginx-mod-stream.x86_64 0:1.10.2-1.el6

完了しました!

nginxをインストールできたことを確認

$ which nginx
/usr/sbin/nginx
$ nginx -v
nginx version: nginx/1.10.2

参考

[Ruby] nilの可能性があるオブジェクトでメソッド呼び出しを安全に行う

例えば文字列に対してlengthメソッドを呼び出すと、その文字数を取得できる

irb(main):001:0> a = 'sasaki'
=> "sasaki"
irb(main):002:0> a.length
=> 6

一方、aがnilだった場合はlengthメソッドが存在しないので例外が発生する

irb(main):001:0> a = nil
=> nil
irb(main):002:0> a.length
NoMethodError: undefined method `length' for nil:NilClass
	from (irb):2
	from /root/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

しかし、変数名の末尾に&をつけることで、対象がnilだった場合メソッド呼び出しを行わずにnilを戻すようにできる

irb(main):001:0> a = nil
=> nil
irb(main):002:0> a&.length
=> nil

nilチェックが不要になるのでコードをスマートに書けるようになる