サーバーサイドプログラマーのためのReact.js 入門 2. 開発環境の構築の続き

これから作るアプリ

今回と次回で作るアプリですが Ruby on Railsのscaffoldジェネレータが生成する古き良き時代の単純なWebアプリケーションと同じ動きをする、React.js を使った Single page application (SPA)のフロントエンドを作っていきます。

バックエアンド(APIサーバー)は Ruby on Rails を使うことにします。

バックエンドの構築

Ruby on Railsのインストール等はネット上の情報を参考にしてください。 私の会社 EY-OfficeのWikiにもインストール方法を書いています (少し古いのでバージョンは最新のものを入れて下さい)

Railsがインストールできたら、以下のようにプロジェクトを作り scaffold ジェネレータを使い、Railsアプリを作ります。

$ rails new todo_app
$ cd todo_app
$ rails generate scaffold todo due:date task:string
$ rake db:migrate
$ rails server

http://localhost:3000/todosブラウザーからアクセスし、New Todo ページでデータを作成したり、アプリの動きを見てください。

f:id:yuum3:20160215151025p:plain

http://localhost:3000/todos.json をアクセスしてみて下さい。 scaffold ジェネレータが作ったコードはデフォルトでJSONを出力できます、これでバックエンドは完成です!

f:id:yuum3:20160215151031p:plain

rails server で起動したサーバーはReact.js でも使いますので、そのまま動かしておいて、以下の作業は別のターミナルで行ってください。

React開発環境の構築

前回は開発環境を構築する前準備でした、今回は具体的な開発環境を作り開発してみます。

Reactに行く前に

Railsの環境ではスタティックなコンテンツ等は public/ に置きます。

そこで JS が入るディレクトリーとJSを表示するための index.html を作成ておきます。

$ mkdir public/js
$ vi public/index.html
  • index.html
<!DOCTYPE html>
<html>
<head>
  <title>ReactTodo</title>
  <meta charset="utf-8">
</head>
<body>
  <div id="example"></div>
  <script type="text/javascript" src="js/app.js" charset="utf-8"></script>
</body>
</html>

React開発環境の構築

今回は Railsプロジェクトの中に frontend ディレクトリーを作りそこで開発することにします。npm init でいろいろとたずねれますが、気にせず return していっても良いです。

$ mkdir frontend
$ cd frontend
$ mkdir -p src/js src/css
$ nam init -y
  • React.js、babel、ESLint、webpack のインストール。 XXXX-loader はwebpack用のモジュールです。

本日ためしたところ eslint 2.0.0 をインストールすると babelが動作しませんでしたので @1.10 でバージョン 1.10.X をインストールしています。非互換な変更があったようです

$ npm install --save react react-dom
$ npm install --save babel-loader babel-preset-es2015 babel-preset-react
$ npm install --save eslint eslint-loader eslint-plugin-react babel
$ npm install --save css-loader style-loader
$ npm install -g webpack 
  • babel 設定ファイル .babelrc の作成。ES6(正式にはES2015)とJSX(React)の変換を行います
{
  "presets": ["es2015", "react"]
}
  • ESLint 設定ファイル .eslintlrc の作成、後で説明します。
{
    "env": {
        "browser": true,
        "node": true,
        "es6": true
    },
    "parserOptions": {
        "sourceType": "module",
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        }
    },
    "extends": ["eslint:recommended", "plugin:react/recommended"],
    "plugins": [
        "react"
        ],
    "rules": {
        "strict": [2, "function"],
        "no-undef": 2,
        "no-console": 0,
        "arrow-parens": [2, "always"]
    }
}
  • webpack の設定ファイル webpack.config.js の作成
    • entry: で生成物 app.js が src/js/index.js から作られることを定義
    • output: で生成物の出来る場所を定義、Rails の public/js/index.js に置かれます
    • PreLoaders: で生成前に ESLintの実行を指定
    • loaders: で生成(変換)作業を定義
      • .js, .jsx に対しては babelで変換
      • .css に対しては、styleタグ追加、css内のurl()等の解決を指定しています
    • resolve: は require/import での拡張子の省略を定義
    • eslint: eslintの設定ファイルを定義しています

設定ファイルの詳細は webpackのドキュメント や各ローダーのドキュメントを見てください。日本語の記事もいくつかありますので、両方を見ながら覚えて行って下さい。 (いずれ記事を書きたいですが、私もまだまだ苦労しています・・・)

module.exports = {
  entry: {
    app: "./src/js/index.js"
  },
  output: {
    path: '../public/js',
    filename: "[name].js"
  },
  module: {
    preLoaders: [{
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loader: "eslint-loader"
    }],
    loaders: [{
      test: /\.css$/,
      loader: "style!css"
    }, {
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
     }]
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.css']
  },
  eslint: {
    configFile: './.eslintrc'
  }
};

試してみよう!

やっと環境ができました、さっそく試してみましょう。 まずは、 Reactの Getting Started のコードを試してみましょう

  • src/js/index.js
var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

ターミナルで webpack -d -w を入力し、少し待つと以下のように表示されます

  • app.js とマップファイルが生成されました。
  • また、index.js では React が定義されているが使われてないというエラーが出ています。
$ pwd
/Users/XXXXXX/todo_app/frontend
$ webpack -d -w                                 
Hash: cf1445dbfe9ce6c676f1
Version: webpack 1.12.13
Time: 2268ms
     Asset    Size  Chunks             Chunk Names
    app.js  764 kB       0  [emitted]  app
app.js.map  846 kB       0  [emitted]  app
    + 180 hidden modules

ERROR in ./src/js/index.js

/Users/yy/tmp/todo_app/frontend/src/js/index.js
  1:8  error  "React" is defined but never used  no-unused-vars

✖ 1 problem (1 error, 0 warnings)

ここでは、ESLintの出したエラーは一旦無視(他のエラーはちゃんと追求して下さいね)してブラウザーhttp://localhost:3000 をアクセスすると 画面に Hello、wold! が表示されます。

f:id:yuum3:20160215151043p:plain

上手くいかない場合はブラウザーのコンソール(開発ツール、インスペクター)を見てください。エラーや警告が出ているかもしれません。

上で書いたJSは ES5 ですが ES6 に変えてみましょう。同じエラーは発生しますが Hello, world! が表示されるはずです

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
)

Next Steps のコードも試してみてください。

ESLint

JavaScriptは動的な言語です、従って実行してみるまでエラーが発見できません。 テストを書いてこまめに開発していていれば良いのですが、ブラウザーを操作してやっと開発中のページを実行した途端に、つまらない文法エラー発生! などということになりがちです。

そこで大きな助けになるのが ESLint です。文法エラー、未定義エラー、定義されているが未使用などを実行前にチェックしてくれるので開発がはかどります!! 特に Reactでは JS の中に HTML が書かれる JSX は、なれるまで間違いが発生しやすのでお勧めです。

ただし、JSは動的な言語なのでチェックは完璧にはできません。上の Hello,world! 出ていたエラーは Reac変数がプログラムの中で使われていないので発生しました。しかし、この行を消してしまうと React.js が読み込まれないので実行できません。

このような場合は、ソースコードか設定ファイルに指定することで回避できます。ここでは .eslintlrc に React は使われてなくても良いというrule を設定を追加してみましょう。 詳しくは Configuring ESLint をみてください。

{
    ... ここまでは同じ...
    "rules": {
        "strict": [2, "function"],
        "no-undef": 2,
        "no-console": 0,
        "arrow-parens": [2, "always"],
        no-unused-vars: [2, {"varsIgnorePattern": "^React$"}]
    }
}

次回は、いよいよ React.js ^^)/

サーバーサイドプログラマーのためのReact.js 入門 1. 開発環境の構築

最近、バリバリと jQueryベースのフロントエンドを React.js に置き換えています。

私も主に Ruby on Rails等の サーバーサイドエンジニアで、最近のフロントエンド開発を本格的に開発するのは初めてです。いろいろとつまずきながら進んできました。

f:id:yuum3:20160204101138p:plain

まずは node.js をインストールし npm を知る

環境構築のためのツールは node.js の上で動きます、そこで node をインストールします。 ツールを動かすだけなので node のバージョンはあまり気にしなくて良いと思います。(2016-02-04日現在ではバージョン5.5.0が入ります)

$ brew install node

node をインストールすると npm コマンドもインストールされます。npmは node.js用ライブラリーと、その管理を行うコマンドです。 Rubyの gem に相当するものですが、さらに Ruby の bundle, rake 的な機能も持っていて node の世界を極めるには非常に重要なものです。

node の生態系は ruby と似ていますが、さらに改良されています。良く使うコマンドは

  • npm init カレントディレクトリーにnpm用設定ファイル package.json ファイルを作ります。このファイルにはプロジェクト(ライブラリー)の情報、依存ライブラリーなどの情報が入っています。コマンドを起動すると name: version: などを尋ねてきますが後で package.json ファイルを変更できるので全てreturnでも構いません。
  • npm install --save ライブラリー名 ライブラリーカレントディレクトリー下に(node_modulesディレクトリーを作り)インストールします。 --save を指定すると依存ライブラリーとしての情報が package.json に入ります。
  • npm install -g ライブラリー名 ライブラリーを共通な場所(brewでnodeをインストールした場合 /usr/local/lib/node_modules)にインストールします。コマンドを含むものは -g (--global) で入れるとコマンドもしかるべき場所に入り便利です。
  • npm --help npm にはたくさんの機能があります、知りたい場合はヘルプを

ビルドツールを選ぶ(知る)

フロントエンドの生態系は凄い速さで進化しています。フレームワークも少し前は AngularJS とか言われてましたが、一昨年あたりから React が主役っぽいし、Reactは表示ライブラリーなので、それを使うためのフレームワークは Flux だとか Redux だとか出来きてます・・・

ビルドツールのようなものには grunt, gulp, babel, bower, browserify, webpack ... とたくさんのもので賑わっていて、何を使えば良いのか迷います。近くにフロントエンドのエンジニアがいれば相談するのが良いと思いますが、やみくもにネットを調べても発散するばかりです !

やりたいことを整理しましょう

まず開発環境の要件というか、やりたい事をまとめましょう。今回の私の要件は

  • React を使う
  • Redux を使うのかな?
  • 現在のJS(ES5)はだるいので、ES6 を使いたい
  • 完全なシングルページアプリ (Rails等のviewは使わない)
  • ブラウザーは最新でけサポートすれば良い
  • 環境構築ツールはシンプルなもにしたい、多少の不便があっても良い
  • HTML,CSSプログラマーが書く(既に出来たHTML/CSSがある)
  • 過去の開発環境にはこだわらない
  • 過去の(デプロイされた)ディレクトリー構成にはこだわらない

一般論としての良いツールではなく自分のやりたい事に最適なツールを探しましょう

ネットで調べてみる

ビルドツール等に関しては qiita を始め多数の日本語の情報がネットから得られので概要を理解するのに役立ちます。しかし最新の正確な情報は本家(英語)のページを読みましょう。

JSX, ES6

ReactではJSXというJSの中に HTML を埋め込んだものを使ういます(使わない方法もあるようですが)。JSXはそのままでは実行できないので通常のJSに変換するツールが必要になります。

var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

また、ES6(正確には ES2015, ECMAScript2015)は全ての主要ブラウザーでサポートされているわけではありません。そこでES6からES5に変換する Babel が良く使われています。

JSXやBabelで変換されたコードをブラウザーで動かすのは良いのですが、デバックはどうするのでしょうか? 変換されたコードでデバックするのでしょうか? 安心して下さい、そのために元のコードでデバックするための ソースマップ(.map) が主要ブラウザーではサポートされています。

ビルドツール

現時点では BrowserifyWebpack が良いようです。

Browserify

Browserify はもともと node.js 用のモジュール構造をブラウザーのJSで使えるようにする技術ですが、それを行うための変換ツールでもあります。また、babelやJSXの変換ツールを統合できます。

Browserifyとnpmを使った最小限のビルドツールの作り方が Reactをnpmでビルドする方法 browserify (watchify) + babelify編 にまとめられています。

npm のスクリプト実行機能を使い以下のように browserify(watchify), babelify を使い ES6(babel-preset-es2015), JSX(babel-preset-react)の変換を行います。また、ソースマップ作成にexorcistを使っています。

watchify -t babelify ./app.js -o 'exorcist ./build.js.map > ./build.js' -d

Browserifyはいつかのツールを組み合わせボトムアップにビルド環境を作るツールです。各種ツールgulpと組み合わせると、自分のやりたいことが実現できると思います。

Webpack

Webpack ホームページの絵からもわかるようにフロントエンドの統合開発ツールです。フレームワークのような作りになっていて、そこに種々のツールプラグインで組み込み設定ファイルで管理します。

新たにトップダウンで作られたツールなのでわかり易いように思います。ただし、自分のやりたいことが完全に実現できないこともあるかもしれません。

試してみた

BrowserifyとWebpackのどちらが良いのか情報でけでは決めかねたので ReactのGetting Started を両方の環境で試してみました。

その結果、今回の要件にはWebpackが良さそうなので使うことにしました。

予告

次回はReactを使った簡単なアプリ作成を Webpack の環境作りから始めます・・・・

Network Link ConditionerのVery Bad Networkは役立つ!

先日、あるサービスでトラブルが !! お客様のところで、ファイルのアップロードが出来ないと連絡がありました。

こちらで試してみると正常にアップロードできます。詳しく聞くと、お客様の自宅からアップロード出来ないとのこと・・・

f:id:yuum3:20160128094525p:plain

Macにはネットワークの速度を制限するツールがある事を思い出し、Network Link Conditioner をインストール ( 詳細はこのページでも参考にして下さい ) し通信速度 3G, Edgeなどにを落としてもアップロードできます。

Profileを見ると Very Bad Network というのがあります、Packets Dropped(パケットロス)やDelay(遅延)に値が設定されています、通信品質が極めて悪いネットワークの設定ですね。

これで、アップロードを試すと確かに全然終わりません !! アクティビティ モニターでネットワークをモニターすると、超低速でアップロードが実行されているようです(転送レートから計算すると10分くらいすれば完了しそうでしたが・・・・)。

ということで、今日の結論 「Network Link ConditionerのVery Bad Networkは役立つ」

Turnip, Cucumber などを使った end-to-endテストのテストデータに付いて

Turnip, Cucumber, RSpec feature などを使った end-to-end テスト のテストデータをどう作るかについて、今までの経験を書いてみます。

RSpecなどを使ったモデルのテストであれば、テストデータは factory_girl 等を使い、テストに必要なデータを it, describe単位でテストで作成する事が多いと思います。

f:id:yuum3:20160108112329j:plain

しかし、end-to-endテストでは人がそのアプリを使う手順、シナリオに沿ってのテストにり、モデルのテストのように個々のテストで個別のデータを必要になることはあまりなく、いくつかのデータを作っておけば済みます。また、モデルのテストでは必要になるモデル(テーブル)は少数ですがend-to-endテストではほとんどのモデル(テーブル)にデータが必要になります。

そこで、今まで作ったアプリのend-to-endテスト用のデータをどのように準備したかを、まとめてみました。またテストデータではありませんが、開発時に使う db_seed についてもふれています。

1. Cucumber + fixture + db_seedなし

Rails 2の頃に作ったアプリで標準の fixture を使っています。なんとモデルも fixture を使っているので fixture はかなりカオスな状況になっています ^^;

また、db_seed については知らなかったので、 rake db:fixtures:load を使っていました。 fixture は人手でデータを作るので テストや開発に必要な意味のあるデータを書くのには向いていると思います。 ただし、テスト用にデータを100件準備するとかには向きません(ERBが書けるので出来ないわけではないですが)。

2. RSpec feature + factory_girl + db_seed

この開発では end-to-endテストは RSpec feature を使い、テスト用のデータは factory_girl を使って作っていますが、helperメソッド内で全モデル用のデータを作り全end-to-endテストで同じものを使っています。 db_seed はActiveRecordメソッドだけで別に作りました。

3. RSpec feature + fixture + db_seedなし

これは、1. のシステムをRails4にバージョンアップする際に管理画面の end-to-endテストを追加した際に作りました。従ってテストデータは 1. と同じです。

4. RSpec feature + factory_girl + factory_girl を使ったdb_seed

db_seed と end-to-endテストのデータは同じもで良いのでは? または db_seed は end-to-endテストのデータ + アルファ なのでは? という仮定のもとに db_seedもfactory_girl を使って作りました。 同じようなものを2つ作らずに済んだので良いアプローチだったと思います。

また、end-to-endテストを作る際には実際にブラウザーを操作し、画面や DOM を見ながらテストを作るのでdb_seed と end-to-endテストのデータが同じだと作業がはかどりました。

5. Turnip + factory_girl + db_seed

この案件は既存のアプリの改善だったので、既に db_seed はあるが end-to-endテストは無い状態だったので end-to-endテストのデータは 2. と同じように作りました。

ただし、 4. に書いたようにテストを書く際にテスト用データでアプリを動かせると便利なので、それを行うツールを作りテストアプリ作成に役立てました。

ツールは以下のようなコードで、load_turnip_seed で Turnip 用に書いたテストデータ作成メソッド turnip_seed_data() を呼び出していま。turnip_seed_data() は通常のfactory_girlを使ったコードです。 テストデータを消すための truncate というメソッドも作っておきました。

# Usage:
# rails runner Tasks::DbTool.truncate
# rails runner Tasks::DbTool.load_turnip_seed

$LOAD_PATH << "#{Rails.root}/spec"  # TODO: もっと良いやり方
require 'turnip_seed_data'

class Tasks::DbTool
  extend FactoryGirl::Syntax::Methods

  def self.load_turnip_seed
    self.truncate
    turnip_seed_data()
  end

  def self.truncate
    ActiveRecord::Base.connection.tables.reject{|t| t == 'schema_migrations'}.each do |table|
      # MySQL only
      ActiveRecord::Base.connection.execute("TRUNCATE TABLE `#{table}`")
    end
  end
end

Turnip を使ったテスト作成のノウハウ

現在、バリバリと Tunip + Capyabara + PhantomJS (poltergeist) で end-to-end テストを書いてます。 そこで知ったノウハウを書きます、今回はテストの構成分けなどの上流側ではなく、ツールや steps 側になります。

f:id:yuum3:20160108112329j:plain

Turnipに限らず Cucmber, RSpec Feature など Capybara を使った、テストの大部分は

  • CSSセレクター(XPath)を使いDOM上のinput、ボタン等を特定し、値の設定やクリックを行う
  • CSSセレクター(XPath)を使いDOM上の表示等で使われる要素を特定し、内容を確認する

の繰り返しです。

ツール

Chrome等のDevTools

まずDOM上のエレメントを特定するための正しく、かつメンテンス生の高いCSSセレクターを決めないと行けません。これは ブラウザーの開発ツール (Cmd+Opt+I)のElements画面で、エレメント選択(Cmd+Shift+C)で画面上の目的の場所をクリックして目的の CSSセレクター を探していけると思います。

また開発ツールの Console を使うと、document.querySelector() や jQueryCSSセレクターが 正しいかを確認できます。 よくあるのは CSSセレクターがユニークでは無く違う要素が選択されてしまう事があるで、単純なCSSセレクター以外は試してみるのが良いです。

正し、jQueryCSSセレクターには拡張があります、これはCapybaraでは動作しないので注意して下さい。

Snapshot

テストが上手く行かない時は表示要素の特定用CSSセレクターの間違だけではなく、対象のページまで来てなかったりJavaScriptが正しく動作してないなどの事もよくあります。 Capybara-WebkitやPoltergeistには現在のDOM(画面)を画像やHTMLとして保存できます。

これを使い以下のようなメソッドやstepsを定義すると、stepsの中や feature の中から呼び出す事で画面を確認できます。

  • helper
def take_screenshot(save_type = :image, path = nil)
  path = Time.now.strftime("/tmp/%y%m%d-%H%M%S-%L.png") unless path

  if save_type == :image
    page.save_screenshot(path)
  else
    File.write(path, page.html)
  end
end
  • steps
step 'HTML保存' do
  take_screenshot(:html)
end

step '画面画像保存' do
  take_screenshot
end

失敗時の Snapshot

以下のような設定を書くと、テストが失敗した時に自動的に Snapshot をとる事ができ失敗の原因追求にとても役立ちます。

RSpec.configure do |config|

  ・・・

  config.after(type: :feature) do |example|
    DatabaseCleaner.clean
    take_screenshot if example.exception.present?
  end

ログ出力

steps は Ruby のコードなので、 p 等でデバック情報をコンソールに出力出来ます。

また、テスト対象の画面にあるJavaScript中に console.log() を書くとやはりコンソールに出力されるのでAjaxからみの複雑な機能のテスト作成に役立ちます。

steps ノウハウ

findの match オプション

page.find()メソッドはデフォルトでは多数がマッチするとエラーになります。CSSセレクターを工夫することでも対処しなくとも、絶対に最初にマッチしたものが正解の場合は以下のように match オプションを指定して逃げる事も出来ます。

  page.find("form input[name='email']", match: :first)

findの visible オプション

page.find()メソッドはデフォルトでは表示されている要素しかマッチしませんが、非表示の要素の値を変更したい場合がありますが、そのような場合は以下のように visible オプションを指定することで可能になります。

  page.find("form input[name='email']", visible: :all)

他のステップの利用

step はrubyメソッドでは無いので、単純には呼び出せませんが、 Calling steps from other steps に書いてあるように、

  • feature に書くように文字列を渡す step メソッド
  • パラメータ(placeholder)を渡し易い send メソッド

の2つを適宜、使い分けると良いと思います。

step 'メール :email パスワード :password ででログイン' do |email, password|
  ...
end

step '社員Aのログイン' do
  step "メール 'a@kaisya.com' パスワード 'PDB4oq4X' ででログイン"
end

step 'ある社員のログイン' do
  send 'メール :email パスワード :password ででログイン',  "e#{rand(1000)}@kaisya.com", "PDB4oq4X"
end

その他

画面サイズ

アプリによってはCapyabara-Webkit画面サイズ(1024 x 768)では一部表示されない場合があるかもしれません。画面サイズは以下のようにして指定できます。

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, window_size: [1280, 1024])
end



ここに書いたノウハウは、検索すると stackoverflow, qiita, ブログ等に書かれています。それらの情報を書いて頂いた方に感謝いたします。

昨年のまとめと今年の抱負

明けましておめでとうございます。

昨年のまとめと、今年の抱負を書きたいと思います。

f:id:yuum3:20160103124424j:plain

2015年のまとめ

お仕事

昨年も開発と教育のお仕事を半々くらいしたでしょうか

スタートアップ企業を中心にいろいろなな会社で Ruby on Railsの教育を行わせていただきました。現在スタートアップ企業では圧倒的なエンジニア不足に悩んでいます。スタート直後はともかく、サービス・プロダクトをリリースし資金調達に成功した会社は将来に備えエンジニアの育成にも前向きに取り組ん行ってほしいと願います。

  • iOSアプリ+バックエンド開発

2014年末より、あるスマフォ系の会社でiOSアプリとそのバックエンドの開発を行わせて頂きました。今までたくさんの人に使ってもらえるようなiOSアプリを作ったことの無い私には、リリースされれば確実に多数の人が使うだろうアプリに気合を入れて取り組みました。そのバックエンド手慣れたRubyを使いGrapeでAPIサーバー、 Ruby on Railsで管理画面を作り、最近はやりのAPIサーバー+スマフォアプリを経験できました。

ただし、アプリのビジネスモデルの関係上 Appleの審査ををなかなか通らず、途中で大きな変更が入ったり、画像をに使った凝ったデザインのiOSアプリ作成に手こずりました。 最終的には残念ながら経営判断でリリースには至りませんでした。もちろん開発費は頂いたので文句はないのですが、自分の作ったものが日の目を見なかったのでは残念です。

  • ボロライディングショップのリニューアル

長年、開発・メンテナンスを続けている ボロライディングショップ のリニューアルを行いました、プログラム的にはクロネコヤマトがカード決済のAPIをサポートしたのでそれを使うことにより使いやすいカード決済を追加しました、実は大きく時間が掛かったのは新デザインへの対応でした。

今までも、デザインはお客様が作ったPhotoshop画像を元に、私の方でHTML、CSSを作っていましたが、今回はお客様が画像、CSS、一部HTMLのブラッシュアップを行いました(最初のHTML、CSSは作りましたが)。お客様は車の会社でデザイナーをされてきた方デザインはベテランですが HTML、CSS は独学で勉強しただけですが、今回のリニューアルは自分でやりたいとの要望もあり共同で開発する事になりました。

お客様の方でGitを使うことは無理そうだったので、デザイン用のサーバーをクラウド上に作り、お客様のMacで作ったCSS、HTML、画像をSFTPするとすぐに確認できるようにしました。昼間はそれぞれの環境で作業を行い、夜に私の方で変更されたCSS、HTML、画像を取り込みGitにマージし結果をデザイン用サーバーにデプロイを行うサイクルで開発を進めていきました。

たった二人で、しかもある程度分業しているのに、離れた場所で作業を行うとコミニュケーションが問題になったりすることもありましたが、最終的には良いものが出来たと思います。 また、今回はカード決済が入った以外に購入ページの流れも変えたのですが、お客様がデザインを変更するためにページを細かくチェックするので、たくさんのバグが早い時点で発見されたのは良い事でした。

以前 会社のブログ に書きましたが、青森のSIerさまで Ruby on Railsの教育を行いました。普段は東京での仕事が多いのですが、たまには地方に行ってみと発見が多いですね。ニュース等で地方都市の空洞化という話題が上がっていますが、実際にそれを見てその切実さを感じました。

  • CookPad 非エンジニア向け教育

これも 会社のブログ に書きましたが、クックパッド様で 非エンジニアの新人にプログラミングの教育を行いました。成長しているネット・IT系企業ではエンジニアだけでなく社員全員がプログラミングについて知っている事は企業の強みにつながると思います。

年末から、大阪のある会社からRuby on Rails + JavaScriptのシングルページアプリのメンテナンスの仕事を引き受けました。JavaScriptの部分は jQueryの上に作られたトータル1万行近いアプリでメンテナンス性が良くないので、ReactJS等の最新のフレームワークの導入を提案したところ通ってしまい、近々ReactJSの置き換えが始まります。現在は置き換え作業を担保するためのEnd to EndテストをTunip + Capybara + PhantomJS で書いています。

スケジュール的にはきついのですが、RecatJSなど新しいものに触れられられのを楽しみにしています。

その他

仕事以外では

  • Macが壊れた

8月に 2011年に買った MacBookPro (15inch)、12月に 2012年に買った Mac mini が壊れました。 MacBookProは無いと仕事が進まないので2015年春のモデルを買いました。Mac mini はCIサーバー等で使っていたのですが、クラウドへ移行して買わずに済ませました。

  • 美食系イベントに参加

ソフトウェアエンジニア界の有名人(?) ogijun さんが主宰する会に参加し、時々おいしいものを食べてます。その結果、体重が着実に増えてます・・・・

  • 久しぶりにワイン会に参加

若い頃にワインにはまり、いろいろなワイン会などの参加していたのですが、この10年くらいは遠ざかっていました。しかし Facbookの東京ワイン倶楽部というのに参加したのをきっかけに久しぶりにワイン会に参加してみました。素晴らしい料理と数々ワインに感動したり、最近のワイン事情を聞けて楽しかったのでが、帰りの駅で貧血になってしまい大変な思いをしました。

参加者の方に助けてもらったのですが、昨年一番の反省事項です。その後は注意して飲むようにしています・・・

2016年の抱負

毎年いろいろと抱負を書いてますが、あまり実現されてません。そこで今年は実現できそうな事を書きます。

  • 情報を発信する

最近、ブログをあまり書いていません。 しかし、上に書いたようにいろいろと新しい事に挑戦したりはしています。最先端ではありませんが、新しい技術を使ってハマった点やその技術について分かり易く書いたりして行きたいと思います。また時には教育の事など普通のプログラマーは経験しない事なども書いていけたらと思います。

いろいろな技術系のイベントの二次会等で、今でもたまに「Yuumiさんのブログの記事で助かりました」という声を聞くことがあります。やはりアウトプットは重要です、地味ではありますが今年はブログなどのアウトプットを続けていきたいと決意しました。

  • ブートキャンプ教育的なビジネス

現在、シリコンバレー では より実践的なプログラミング教育を行う MakeSchool のようなブートキャンプ教育がブームになっているようです。 一昨年の TechCrunch Tokyo のイベントで会った MakeSchool の創始者 Jeremy Rossmann のプレゼンに影響を受け、私も ブートキャンプ教育的なビジネス に付いてある方とお話をしました。しかし、それ切りになってしまいました。

昨年末の楽天のイベントで Jeremy Rossmann に再会できたこともあり、今年はこれをもう少し形あるものにしたいと思っています。

f:id:yuum3:20160101102642j:plain

今年のお正月で飲んだシャンパン

DockerでRuby on RailsのCI環境を作ろうとした話

少し前にCIサーバーとして使っていたMac miniがお亡くなりになり、CIサーバーどうしようと考えていました。 Dockerを使いCI環境を作り、さくらのクラウドにあるステージング用のサーバーで動かせば良いのでは! と思い立ち土日に挑戦してみました。

Docker

Mac上でCI環境を作る

Docker ToolboxをインストールすればDockerMachineやDocker Client(dockerコマンド)、VirtualBoxなどがインストールされます。

とりあえず、何も考えず docker-machine コマンドで Docker用のサーバー(環境)がVirtualBox上に立ち上がります。

$ docker-machine create -d virtualbox

オプションを指定してDockerサーバーメモリーやCPU数を増やしたりもできます。

後は docker コマンドを使い、Dockerイメージを作ったり、走らせたりして環境を作ります。 Dockerイメージへのアプリのインストール等の作業はDockerファイルに書きます。

CI環境作り

今回作るのは普通のRuby on RailsアプリのRSpecをJenkins上で実行します。そこで必要になるものは、

Rubyのインストールは Jenknsの rbenv Plugin がインストールしてくれるので環境には、OS標準のRubyを入れておけば良いです。

Ruby on Railsはご存知のように bundler がインストールしてくれるので、C言語拡張ライブラリーが構築できる環境があればOKです。

1. Jenkinsの公式Dockerイメージ

Jenkinsの公式Dockerイメージ が公開されています。これをベースにすれば楽じゃないか!

当然、Jenkinsは動きますが PostgreSQLインストールで ja_JP.UTF-8 ローケールを作ろうとしたのですが上手くいきません・・・ いろいろとトライしたのですがベースのUbuntuが通常とは異なるようであきらめました。

2. Java Runtime Environment がインストール出来ない !

JenkinsはJavaで出来ているので Java Runtime Environment (JRE) が必要です。apt-getでJenkinsをインストールすると依存関係から OpenJDK がインストールされるのですがインストール中の

Setting up ca-certificates-java ....

で止まってしまいます。仕方ないので、まずは OracleJREを以下のようなDockerfileインストールしていました。

RUN apt-get install -y software-properties-common
RUN add-apt-repository -y ppa:webupd8team/java
RUN apt-get update
RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y oracle-java7-installer

でもなぁ〜 と思い、いろいろ検索していたら qiita に解決方法がありました。 Dockerサーバーを作るさいに以下のようなオプションを指定すれば上手くいきます。

$ docker-machine create --driver virtualbox --engine-storage-driver overlay dev

3. やっぱりsshdは必要

Docker内にはsshdを立ち上げてsshログインしたりせず、ログ・ディレクトリー等をホストのディレクトリーにバインドシして使うべきだ! という考え もあるようですが、やはり上手くいかない時の調査ようにsshログインできた方が良いですよね。

USER root
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:パスワード' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

を Dockerfileの最後に書いておくとsshログイン出来ます。rootでログインするのは気持ち悪いですが・・・ 最終的な環境でネット上からはsshできないようにiptables(Firewall)等で制限しておいて下さいね。

4. 出来ました!

Dockerfile は以下のようになりました

FROM ubuntu:14.04
USER root

# IMPORTANT http://qiita.com/moutend/items/18682948811175ed0e1d
# docker-machine create -d virtualbox --engine-storage-driver overlay NAME

RUN apt-get update 
RUN locale-gen ja_JP.UTF-8
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

## Libs
RUN apt-get install -y wget git-core ruby build-essential bison flex sqlite3 autoconf  \
 libreadline6-dev zlib1g-dev libssl-dev libyaml-dev libxml2-dev libxslt1-dev libncurses5-dev libsqlite3-dev

## Tools
RUN apt-get install -y fonts-takao-mincho libfontconfig1-dev libicu-dev libfreetype6 libpng-dev libjpeg-dev
RUN wget -O /usr/local/bin/phantomjs https://github.com/Pyppe/phantomjs2.0-ubuntu14.04x64/raw/master/bin/phantomjs
RUN chmod 755 /usr/local/bin/phantomjs 

## PostgreSQL
RUN apt-get install -y postgresql-9.3 libpq-dev postgresql-client-9.3 postgresql-contrib-9.3
RUN sed -i 's/local\s*all\s*all\s*peer/local\tall\tall\tmd5/' /etc/postgresql/9.3/main/pg_hba.conf
USER postgres
RUN /etc/init.d/postgresql start &&\
    psql --command "CREATE USER テストアカウント WITH CREATEDB PASSWORD 'パスワード';" &&\
    psql --command "CREATE DATABASE テスト用データベース WITH OWNER テストアカウント TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'ja_JP.UTF-8' LC_CTYPE 'ja_JP.UTF-8';"

## Jenkins
RUN wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
RUN sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
RUN apt-get update
RUN apt-get install -y jenkins

## SSH
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:パスワード' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
EXPOSE 22

## Startup
COPY startup.sh /usr/local/bin/startup.sh
ENTRYPOINT ["startup.sh"]
  • startup.sh は
#!/bin/bash

/etc/init.d/postgresql start
/etc/init.d/jenkins start

/usr/sbin/sshd -D

5. クラウド上のUbuntuで動かない!

クラウド上の UbuntuサーバーにDocker環境 を作り、Mac上で作ったDockerイメージをsave, load したのですがなぜか PostgreSQLが起動しません (Jenkins, sshd は起動されます)。

# /etc/init.d/postgresql start
Starting PostgreSQL 9.3 database server                                                                                                                       * The PostgreSQL server failed to start. Please check the log output:
2014-12-01 17:41:37 UTC FATAL:  could not access private key file "/etc/ssl/private/ssl-cert-snakeoil.key": Permission denied

調べてみると、ここにissue がありました。 しかし、解決しませんでした・・・・ 疲れた ・・・

Dockerの完成度はこんなものなのでしょうか???

今回の結論

というわけで、ステージングサーバーにJenkinsを直接インストールしました ^^);

ステージングサーバーのruby(gem)は /usr/local/ にインストールされているので、Jenkinsのrbenvとは独立しています。RDB(PostgreSQL)は共有されますがtest用databaseは別なので問題なく同じサーバーで動きますした。