ホーム ブログ ページ 25

jQuery.ajaxでRailsのコントローラを叩く

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

ajaxを利用することで、jQuery側からRailsのコントローラへアクセス出来たので備忘録を兼ねて記載しようと思います。

gemの導入

railsでjQueryを扱えるように、gemを導入します。

(デフォルトで入ってる場合もあるみたいです。)

# Gemfile
gem 'jquery-rails'

Controllerの用意

# tests_controller.rb

def index
end

#ユーザの一覧をjson形式で返すアクション
def ajax_test
  @user = User.all
  render json: @user
end

ページ表示用のindexアクションと、ajax通信用のajax_testアクションを定義したControllerを用意します。

対応するルーティングの追加

ajaxでアクセス出来るように、ルーティングを追加します。

# config/routes.rb
Rails.application.routes.draw do
  resources :users
  resources :tests
  get 'ajax_test', to: 'tests#ajax_test'
end

Viewの用意

# index.html.erb

<% content_for :head do %>
  <%= javascript_include_tag 'ajax_test', 'data-turbolinks-track' => true %>
<% end %>

<h1>ajaxテストページ</h1>

<%= link_to 'jsonを表示する', ajax_test_path, {id: "ajax_test", remote: true} %>

<table id="user"></table>

indexアクションでrenderするviewファイルを用意します。

今回は、リンクをクリックしたらajax通信が始まるようにしました。

(remote: trueを追加することで、rails側がajax通信をしてくれるようになります。)

coffeescriptの用意

ajaxでアクセスするためのcoffeescriptを用意します。

リンクをクリックしたら、動作するように指定しました。

また、ajax通信が成功したら、返却されたjson形式のデータをtableタグの内部に追加するようにしています。

# ajax_test.js.coffee

$ ->
  $("#ajax_test").click ->
    $.ajax
      url: '/ajax_test'
      type: 'get'  #get,postの指定
      dataType: 'json' #レスポンスのデータタイプ指定
      async: true #非同期通信フラグ
    .done (response) ->
      #処理が成功した時の処理
      $("#user").append("<tr><th>名前</th><th>年齢</th></tr>")
      for i in [0..response.length]
        $("#user").append("<tr><td>" + response[i].name + "</td><td>" + response[i].age + "</td></tr>")
    .fail (response) ->
      #処理が失敗した時の処理
      alert 'fail'

# 生成されるjavascript

(function() {
  $(function() {
    return $("#ajax_test").click(function() {
      return $.ajax({
        url: '/ajax_test',
        type: 'get',
        dataType: 'json',
        async: true
      }).done(function(response) {
        var i, j, ref, results;
        $("#user").append("<tr><th>名前</th><th>年齢</th></tr>");
        results = [];
        for (i = j = 0, ref = response.length; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
          results.push($("#user").append("<tr><td>" + response[i].name + "</td><td>" + response[i].age + "</td></tr>"));
        }
        return results;
      }).fail(function(response) {
        return alert('fail');
      });
    });
  });

}).call(this);

実際に動作させてみる

railsを起動して、期待通りに動作するか確かめました。

enter image description here
enter image description here
上記の「jsonを表示する」リンクをクリックすることで、無事テーブル内にjsonの中身を表示することが出来ました。

まとめ

jquery側からControllerへ、ajaxを利用すればアクセス出来ることが分かりました。

まだ単純なアクセスと表示でしか利用出来ていないので、もっと他に便利なことが出来ないか模索してみたいと思います。

壁貫通バグ!?現実世界の不思議な仕様

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回は現実世界のバグのようなものについてご紹介します。

はじめに

ゲームをプレイしていると、たまに出会うことがあるバグ。その中の一つに、本来通り抜けられない壁を越えてしまえるバグなんてものがあります。
私にも、四天王の最後のフロアで柱になみのりを…なんてバグで遊んでいた記憶があります。
ゲーム制作側としては、バグなんてあってはならないものですが、ゲームをプレイする側で考えると、見つけられるとちょっと楽しくなりませんか?
今回の記事は、ゲームではなく、現実世界で、壁抜けバグのような現象があるようなので、それを紹介したいと思います。

ミクロな世界

今回紹介する現象は、非常に小さいミクロな世界で確認されている現象なので、まずはミクロな世界について少しお話します。(逆にこの記事では私たちが普段見ている世界をマクロな世界と呼びます。)
物体を、その構成要素に分けていくと、最終的には非常に小さい粒子にたどり着きます。
例えば、水は化学式でH2Oと書かれますが、これを分けていくと次のようになります。
水 ⇒ 水分子 ⇒ 酸素原子、水素原子 ⇒ 陽子、中性子、電子
この陽子、中性子、電子のような非常に小さい粒たちが今回紹介する現象の主役になります。
ちなみに、どれぐらい小さいかという有名なたとえとして、
ピンポン球の原子とピンポン球の大きさの比は、ピンポン球と地球の大きさの比と同じぐらいだそうです。
enter image description here

トンネル効果

名前の通り、壁を通り抜けそうな名前ですが、今回の現象の名前が「トンネル効果」です。
ミクロな粒子たちは、常に動き回っているため、「どこにある」という表現ではなく、「どこらへんにいそう」という表現をします。
つまり、場所がきっちり決まるのではなく、場所を存在確率で表現します。「ここにいます!」というのではなく、「この辺に良くいます!」といった言い方でしょうか。
ミクロな粒子について、粒子を壁ぶつけることを考えると、壁の向こう側の存在確率が0にならないことがあります。
つまり、壁の向こう側にも粒子が存在できてしまうのです。これをトンネル効果と呼びます。
私たちが見ているマクロな世界では、壁がどんなに薄くても、物体が壁の向こうに行くことはまず考えられません。
しかし、上述のように、マクロな世界は、ミクロな世界の粒子が多数集まって構成されており、そしてミクロな粒子一つ一つには壁を超える可能性を持っています。つまり、非常に低い確率ではありますが、マクロな世界でも壁を超える可能性があるということになります。

※ちなみに、この「トンネル効果」はすでに半導体や顕微鏡などに応用されている現象です。
顕微鏡に応用されている例を紹介しますと、トンネル効果を応用した顕微鏡は「走査型トンネル顕微鏡」と呼ばれ、細い金属針を物体表面に近づけて、トンネル効果で電流が流れてくるときの電流の強さによって、物体表面の原子レベルの凹凸まで判断できる超高精度な顕微鏡です。
enter image description here

人間が壁をすり抜ける確率概算

ネットを調べてみても、現実世界のトンネル効果について言及している記事があまりなかったので、ここでは私が適当な数字を当てはめて計算してみようと思います。
調べたところ、人間の体重の65%程度は酸素原子の重さのようなので、簡単のため人間が100%酸素で構成されていると考えます。
ここでは酸素原子一粒が壁を越えられる確率を10%とします。(酸素原子はすでに陽子、中性子、電子の集合体なので、実際にはもっと低いはずです。)
酸素原子Oの原子量は16なので、6*1023 個集まると16グラムになります。
体重64キロの人間を考えると、体重が酸素の原子量の4000倍なので、4000×6×1023 個=2.4×1027 個、酸素原子が集まっていることになります。
後は、これらが通り抜ける確率50%を一斉に引ければいいので、
0.1の2.4×1027 乗を計算すれば良いということになります。
これを直接電卓に入れるとエラーになって答えが出ないので、概算します。
2.4×1027 ≒1027 として、0.11027なので、0.の後ろに0が1027 個続くぐらい小さな確率です。
では、どのぐらいの確率なのか行動に移してみましょう。
仮に、宇宙が始まってから今日まで1秒に一回ずつ壁にぶつかり続けるとしましょう。宇宙を約140億年とすれば、1年は3*107 秒なので140億年では4.2*1017 回壁にぶつかれます。
すると、すり抜けられる確率は、4.2*1017 / 1027 ≒1/1010 ⇒100億分の1ということで、このペースだと100億分の1程度ということになります。
つまり、1秒間に100億回かべにぶつかりつづければ、宇宙が始まってから現在までに1回ぐらい壁抜けが出来るかもしれませんね。

終わりに

いかがでしょうか。自分の概算ではありますが、宇宙始まってから毎秒100億回かべにぶつかってくれるデバッカーがいないと気づけないバグなんて大変なことですね。
これがバグなのか仕様なのかは創り手のみぞ知るということで、この世界にはまだまだ不思議な仕様が眠っていそうでわくわくします。

カフェインはどうやって目覚まし効果を発揮するのか

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

はじめに

1年間に世界で消費されるカフェインの量は10万トンに及び、カフェインのほとんどはコーヒーやお茶から摂取されています。

眠気覚ましにコーヒー、栄養ドリンクを飲む人の多くは、コーヒーなどに含まれる「カフェイン」の効果を狙っているわけですが、一体カフェインはどのようにして目覚まし効果を発揮しているのか?
飲み過ぎるとどうなるのか?
調べてみました。

カフェインによる体の影響

カフェインを摂ると、注意深くなり、集中力が増し、幸せで精力的になれます。
睡眠不足の状態でも効果を発揮しますが、心拍数と血圧を上げて、不安な気分にもなります。

カフェインは人間の体内では神経システムを刺激する効果をもたらし、
眠気の鍵となる物質の働きを妨害することでカフェインは眠気を覚まします。

ちなみにカフェインは摂取してから、効果があらわれるまでには約30分かかり、その効果は約5時間-7時間ほど続きます。

また、カフェインを長期間摂取することはパーキンソン病やアルツハイマー病や特定の癌などの病気に罹患するリスクを下げる効果や、体の脂肪を燃やす機能を助けることがあることも分かっています。

カフェインは良い事だけじゃない

カフェインは眠気を解消したり、病気のリスクを下げたり、はたまた脂肪燃焼もしてくれる大変ありがたいものですが、
体にとって良い効果ばかりではありません。

カフェイン接種により集中力が高まると同時に、心拍数は多くなり血圧は上昇します。
また、利尿作用や下痢や不眠をもたらしたりします。
カフェインを日常的に摂ると、脳はその状態に適応してきます。

そこで、眠気を覚まそうと、さらにカフェインの摂取量を増やすという行動につながります。

カフェインの摂取を急に止めると、頭痛や倦怠感、落ち込んだ気分を生じさせます。
そしてしばらくすると体からカフェインが抜けて体はリセットされるので、
昔のようにカフェインの目覚まし効果が復活することになります。

まとめ

体質的な相性や、当日の体調によっても体への影響が変わりますので、上手にカフェインを摂取して付き合っていくのが良い
何でも程よいくらいがちょうどいい

セオリーを知る ~プランニング編~

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

前回はプレイヤー視点でのセオリーの重要性を説明した。 今回はそのセオリーを踏まえて新しくゲームを作るのは どうするかを考える。

セオリーを守る

 前回に引き続き、対戦カードゲームを例に考えていく。
 前回は「いかに盤面にモンスターを残して相手に攻撃を通すか」というのが基本であることを説明したが、もし「相手プレイヤーに直接ダメージを与えるカードをたくさん入れるのが強い」場合どうだろうか。これは対戦カードゲームの醍醐味である「プレイヤー同士の駆け引き」を放棄し、壁とやっているのと大差ない状態になることが目に見えるだろう。
 このような「当然そうあるべき暗黙の了解」を守ることは土台として必要となる。

新しいセオリーを生み出す

 セオリーを守るだけではユーザーからは当然見向きもされない。他のゲームをやるのと変わらないからだ。
 例えば「同じカードが手札にある場合、1枚捨てることで残っている同じカードのコストを減らせる。これは対戦中3回行える。」というルールがあったとしよう。このとき、「できるだけ同じカードを入れる」「あまりやりすぎると手札のリソースが無くなってジリ貧になる」といった要素が加わる。そのうち、強いプレイヤーが「現環境ではどうするのがベストか」というセオリーを生み出すだろう。
 そう、セオリーはそのときのカードやゲームのルールに応じて刻々と変化するし、ユーザーが見出すこともあるのだ。

セオリーを破る

 最後にセオリーを正しく破ることの重大さを知っておこう。
 デジタルカードゲームが台頭してきたとき、リアルのカードゲームと決定的で絶対的な差に驚いたことがある。遠くにいる誰とでもいつでも対戦できるとかそんなことではなく、リアルのカードゲームでは非現実的な要素だ。それは「トークンカードの生成」「ランダム実行」というデジタルならではのものである。
 「2種類のモンスターの能力を合わせたカードを作る」「存在するあらゆるカードからランダムなカードを発動する」なんてことはデジタルでないとまず不可能と言える。これは「デッキに投入したカードだけで戦う」というセオリーを「デッキ外から追加されるカードも織り込んで戦う」にする進化を対戦カードゲームに与えた。もちろん広い意味では「デッキに投入したカードだけで戦う」は維持されている。

まとめ

 セオリーや暗黙の了解といったものは言語化されづらく、どこまでが当てはまるかは人によって異なるだろう。今まで挙げた中にもセオリーという言葉で示されるべきではないものも含まれているかもしれない。
 しかしここまでで伝えたかったのは「一般的な共通認識は非常に重要である」という一点に尽きる。ここを疎かにすると手痛いダメージを受けることになるだろう。

APIを叩いてslackから発言を取ってくる

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

目次

  • 概要
  • Slack API
  • トークンに関して
  • コード

概要

パワーメタルやシンフォニックメタルが最近のお気に入りです。
新卒はslackにDorubyのネタになりそうなアイデアの種を雑に書き込むチャンネルを作ってます。
が、slackの無料版では10000メッセージまでしか見られないので、今月の記事を書こうとしたら過去のネタが消えているという悲しい出来事がありました。
もう悲しい思いはしたくないのでslackの投稿を定期的に保存しておこう。というのがこの記事のテーマです。

Slack APIを叩く

Slackのチャンネル履歴のAPIのドキュメントです。
https://api.slack.com/methods/channels.history
一番簡単に使うには、TokenとチャンネルIDだけパラメーターで与えてやるといいです。
チャンネルIDは
https://api.slack.com/methods/channels.list/test
から取ってきてもいいし、チャンネルリストAPIを叩いてもいいです。
https://api.slack.com/methods/channels.list

トークンに関して

ここまではとても簡単なのですが、Slackのトークン関連で詰まりまくりました。
SlackのAPIトークンには2種類あって、
1. レガシートークン
2. レガシートークンじゃない方の普通のトークン
です。
レガシートークンは
https://api.slack.com/custom-integrations/legacy-tokens
から取得できますが、これではchannels.history APIは取得できないので、後述する普通のトークンを取得してください。
ちなみに、channnels.list APIは普通に叩けるので、ここで理解ができなくて詰まりました。
レガシートークンじゃない方の普通のトークンの取得
https://api.slack.com/apps
ここからAppを作成すると普通のトークンが取得できます。
Add features and functionality -> Permissions -> Select Permission Scopes からchannels:historyを選択します。
そうすると、channels.historyが選択できるようになります。

コード

# python3.6
import requests
import json
url = "https://slack.com/api/channels.history"
token = "{YOUR TOKEN}"
channel_id = "{YOUR CHANNEL ID}"
def main():
    payload = {
        "token": token,
        "channel": channel_id
        }
    response = requests.get(url, params=payload)

    json_data = response.json()
    messages = json_data["messages"]
    for i in messages:
        print(i["text"])
if __name__ == '__main__':
    main()

これを実行すると

xonsh
gitkraken
Progressive Web Apps
スクレイピング RSS
ARCore
Pyro
tse
スマートスピーカー
Prophet
Python Exception
Superset
Android Architecture Component
Headless Chrome
ラズパイ NFC
LiveData
Flux
Quantum CSS
IRKit
Hue
Puppeteer

みたいなメッセージがとれます。
コードは簡単すぎて何も書くことが無いんですが、pythonの通信ライブラリはrequestsが好きです。

無駄に見えて無駄じゃないalias

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは、幾田です。

今回は「無駄に見えて無駄じゃないalias」と題して、私が趣味で登録しているbashのaliasをご紹介します。
やってることは無駄ですが、シェルスクリプトのお勉強になるので無駄じゃありません。

1. :q

私はテキストエディタにvimを使ってます。
間違えてvimの終了コマンド:qをbashで打ってしまうことがあります。

# ~/.bashrc

alias :q='echo ここvimじゃないよ:p'
q.gif

忙しい時に見るとだいぶ腹がたちます。
aliasに記号:が使えることがわかりましたね。

2. lll(小文字のL)

llのaliasを登録している人は多いのではないでしょうか。
同じ文字2文字だし、ls -lの略でllってわかりやすいですよね。

でも、勢い余ってlllと打ってしまうことがあります。
連打してしまってもある程度大丈夫なようにaliasを設定しましょう。

# ~/.bashrc

# llコマンドのaliasを設定します
alias ll='ls -l'

# ベースとなる文字列を宣言
l_length="ll"

# for文を使って10回繰り返す
for i in `seq 10`
do
  # 文字列の末尾に一つlを追加していく
  l_length=$l_length"l"

  # 文字列に対してlsのaliasを設定
  # `echo $l_length` の実行結果をaliasに渡す
  alias `echo $l_length`='ls -l'
done
ls.gif

これで最大llllllllllll連続12文字まで登録できました。
何が嬉しいのかわかりませんが、aliasに登録するコマンドに変数を使う方法がわかりました。

3. 眠い

業務中眠いなあと思ってbashに眠いと打ちます。

$ 眠い
-bash: 眠い: command not found

そんなコマンドはないので怒られます。
怒られるなら「仕事しろ」と叱咤激励してもらいたいものですね。
というか怒られるよりもこの眠さを誰かにわかってほしい。
この睡魔を同意してもらえるようにaliasを設定します。

# ~/.bashrc

# ランダムに同意の言葉を返す関数 sympathize_randomly
function sympathize_randomly () {
  # 「眠い」に返す言葉の配列 replies を宣言
  declare -a replies=(
    "私も"
    "俺も"
    "僕もです"
    "仕事しろ"
    "わかるぅ〜"
  )

  # ランダムな数字を配列の要素の個数で割った余りを計算
  random_number=$[1] $RANDOM % ${#replies[@]} 

  # 配列からランダムな要素を出力
  echo ${replies[$random_number]}
}

# sympathize_randomly 関数を使ってaliasを設定
alias 眠い='sympathize_randomly'
sleepのコピー3.gif

aliasに日本語も設定できるんですね。
関数の宣言の仕方や、配列の要素数の取得方法などいろんなことが学べました。

alias 設定時に気をつけること

aliasの設定で気をつけるべきは既に存在するコマンドを登録してしまわないことです。
また、aliasが便利だからって何でもかんでも設定していると、どれが何かわからなくなってしまうこともあるかもしれません。
破壊的なコマンドを簡単なaliasに設定するのもよくないです。

まとめ

やっていることは大変無駄でしたが、bashの知識はいつかどこかで役に立ちそうです。

それでは、今日はこの辺で。
読んでいただき、ありがとうございました。

References

References
1 $RANDOM % ${#replies[@]}

javascriptでダイアログを表示する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

 webサービスで、ユーザに確認・注意させる場合に利用する手段としてダイアログの表示が存在します。今回は、javascriptで使える標準のダイアログについてまとめてみました。
OSは、Windows 10。ブラウザは、Firefox Quantum 57.0を使用しました。他のブラウザでは、こちらの記事とは異なる動作になる恐れがあります。

alert

 alertは、ユーザに注意を促す場合に使用するダイアログです。OKボタンを押すとダイアログが閉じます。

enter image description here

 以下のコードで、上図のダイアログを表示させることができます。第一引数に表示させたい文字列を指定します。第二引数以降に文字列を設定しても第一引数のダイアログのみ表示されます。

alert('メールアドレスを入力してください。');

confirm

 confirmは、ダイアログにOKボタンとキャンセルボタンが備わったダイアログです。OKボタンとキャンセルボタンの押し分けで動作を分岐させることができます。

enter image description here
confirm('アンケートが入力されておりません。次の画面に進みますか?');

 confirmは、OKボタンを押すとtrueをキャンセルボタンを押すとfalseを返します。以下のコードにすることで、分岐が可能になります。こちらもalertと同様に、第一引数のみダイアログに表示されます。

var result;
if(confirm('ボタンを押してください。')) {
  result = 'OKボタンが押されました。';
} else {
  result = 'キャンセルボタンが押されました。';
}
alert(result);

prompt

 promptは、テキストボックスが備わったダイアログです。promptの返り値は、ダイアログで入力した値です。何も入力しなかった場合は、nullが返ります。

enter image description here
prompt('メールアドレスを入力してください。');

 promptに第二引数を指定すると、ダイアログのテキストボックスに入力された状態で表示されます。

var input = prompt('メールアドレスを入力してください。', 'test@example.com');
alert(input + "が入力されました。");

ダイアログの表示を抑止

 ダイアログを短い間隔で表示させると、以下のダイアログが表示されます。今まで紹介したダイアログには表示されなかったチェックボックスこのページによる追加のダイアログ表示を抑止するの文言が追加で表示されます。このチェックボックスを入れた状態でダイアログを閉じると、次からのダイアログが表示されなくなります。この機能が存在する理由は、ダイアログを無限に表示させるブラウザクラッシャー対策です。
※promptダイアログで、チェックボックスをいれた状態でキャンセルボタンを押した場合のみ、ダイアログが次も表示されました。

enter image description here
 ダイアログが表示されなくなることで、confirmが期待通りの動作が行われない場合があります。ダイアログの表示を抑止された状態だと、confirmは必ずfalseを返します。よって、抑止状態だとOKボタンを押した時の動作を行うことができなくなります。この問題を発生させないには、「ダイアログ表示するときに一定時間の間隔を設ける」や「自作のダイアログを使用する」を行うことで回避できます。

まとめ

javascriptの標準ダイアログの利点

  • 簡単に実装することができる

javascriptの標準ダイアログの欠点

  • カスタマイズできる項目がダイアログの文言のみ
  • ダイアログの表示の抑止した状態を考慮する

“同じ文字列”==”同じ文字列” => false

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

TL;DR

表示上は全く同じように見える文字列同士が==演算子でfalseを返すことがある。一つの原因として考えられるのはUnicodeが合成文字という仕組みにより濁音/半濁音を別扱いしているというもの。Unicode、また君か? 解決方法はUnicode正規化をすること。例えばrailsのActive Supoortなら以下のように正規化できる。

ActiveSupport::Multibyte::Unicode.normalize(string, :kc)

現象と原因の特定

この現象に遭遇したのはxmlからパースした文字列をrspecで期待値通りかチェックしていた時のこと。このように表示上は同じように表示されるが==(eq)演算子ではfalse扱い。

str1
#=> "電話番号 : 入力データが不正です"
str2
#=> "電話番号 : 入力データが不正です"
str1 == str2
#=> false

そこで、まずはバイトサイズを確認すると一致しない。そこでエンコーディングの差を疑って文字列のエンコーディングを確認するがこれも一致する。

[str1.bytesize, str2.bytesize]
#=> [54, 45]

[str1.encoding, str2.encoding]

#=> [#<Encoding:UTF-8>, #<Encoding:UTF-8>]

では何が違うかというと前述したとおり、str1では濁音/半濁音の記号が分離されている。これはString#unpackで文字列をコードポイントに変換したり、String#charsで文字単位に分割するとわかる。

str1.unpack("U*")
#=> [38651, 35441, 30058, 21495, 32, 58, 32, 20837, 21147, 12486, 12441, 12540, 12479, 12363, 12441, 19981, 27491, 12390, 12441, 12377]
str2.unpack("U*")
#=> [38651, 35441, 30058, 21495, 32, 58, 32, 20837, 21147, 12487, 12540, 12479, 12364, 19981, 27491, 12391, 12377]

str1.chars
#=> ["電", "話", "番", "号", " ", ":", " ", "入", "力", "テ", "゙", "ー", "タ", "か", "゙", "不", "正", "て", "゙", "す"]
str2.chars
#=> ["電", "話", "番", "号", " ", ":", " ", "入", "力", "デ", "ー", "タ", "が", "不", "正", "で", "す"]

これにはUnicodeの合成文字という仕組みが関係している。合成文字/結合文字(combining character)は非結合文字の直後に結合文字を配置することで結合文字列(CCS)を作る仕組みのこと。簡単にいえば「が」という文字列を「か(非結合文字)」「<濁音記号>(結合文字)」という2つのコードポイントで表してもいいということだ。ややこしいのが、Unicodeは「が」という一つのコードポイントで表しても良い点で、つまり同じ文字を表すのに異なるデータの表現方法がある。

当然、バイト列が違うのでそれぞれの表現でバイト数さえ食い違う。このままでは色々な問題が発生するため、これらのバラバラな表現を統一するように正規化することが出来る。

str1.bytesize
#=> 54
ActiveSupport::Multibyte::Unicode.normalize(str1).bytesize
#=> 45
ActiveSupport::Multibyte::Unicode.normalize(str1) == str2
#=> true

ちなみにUnicode正規化にはNFD、NFCなど複数の形式があり、上記のコードでは省略しているが、normalizeメソッドは2番めの引数に正規化形式を指定することが出来る。ex. normalize(str, :nfc)

ところで色々な問題が発生する、と書いたが面白いのはセキュリティに関する問題を引き起こすこと。文字列の比較はセキュリティに深く関わるトピックであり、XSSを防ぐためのエスケープにせよ、パスワードの認証にせよ、文字列が正しく比較出来ることを前提にしている。その前提をいじれてしまうのなら色々なことが出来てしまう。

考えられる他の原因

表示上は同じだがデータは異なる、という文字列比較のややこしい問題は他にも色々ある。やはり文字列データは非常に複雑なデータ形式ということだろうか。こういった問題が不完全な抽象化から抜け出せる日はまだ遠い。主が怒り全地の言葉を乱し人を全地に散らされたために人々は当面の間は苦しむ。

  • 非最短形式
  • 多対一の変換
  • 大文字と小文字
  • エンコード情報の不一致
  • 7ビットエンコーディングの解釈

CentOS6環境にZabbixをインストールしてみた

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

ローカル環境でZabbixの動作を確認するためにVagrant上のCentOSにZabbixサーバを構築したときの手順を公開します。

前提と注意

注意

以下の手順はローカル環境での動作確認を目的とした構築手順でありセキュリティ的な考慮は行ってません。
本番で稼働させるにあたってはセキュリティに配慮した設定を意識してください。

Vagrant環境

Vagrant環境にCentOS6を入れて環境構築しています。

以下のように、private_network でホストOSからアクセスできるようにしておきます。

メモリはなんとなく1Gにしてますが、お試し用途であればデフォルトの512MBでも動くかもしれません。

Vagrant.configure("2") do |config|
  config.vm.box = "centos/6"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end
end

バージョン

OS/ミドルウェアのバージョンは以下のとおり。

  • OS: CentOS release 6.9 (Final) ※Vagrantの仮想環境
  • Zabbix: 3.0
  • MySQL: 5.1.73
  • PHP: 5.5
  • Apache: 2.4

手順

ここからは vagrant up で起動した仮想マシンでの作業です。

SELinux無効化

動作確認目的なのでSELinuxを無効化する
/etc/selinux/config を以下のように編集して仮想マシンを再起動します。

SELINUX=disabled

yumリポジトリ追加

以下のように Zabbix 公式のyumリポジトリを追加します。

# rpm -ivh https://repo.zabbix.com/zabbix/3.0/rhel/6/x86_64/zabbix-release-3.0-1.el6.noarch.rpm

追加後、zabbix を検索したら以下のように表示されるはずです。

# yum search zabbix
Loaded plugins: fastestmirror

zabbix-agent.x86_64 : Zabbix Agent
zabbix-get.x86_64 : Zabbix Get
zabbix-java-gateway.x86_64 : Zabbix java gateway
zabbix-proxy-mysql.x86_64 : Zabbix proxy for MySQL or MariaDB database
zabbix-proxy-pgsql.x86_64 : Zabbix proxy for PostgreSQL database
zabbix-proxy-sqlite3.x86_64 : Zabbix proxy for SQLite3 database
zabbix-release.noarch : Zabbix repository configuration
zabbix-sender.x86_64 : Zabbix Sender
zabbix-server-mysql.x86_64 : Zabbix server for MySQL or MariaDB database
zabbix-server-pgsql.x86_64 : Zabbix server for PostgresSQL database
zabbix-web.noarch : Zabbix web frontend common package
zabbix-web-mysql.noarch : Zabbix web frontend for MySQL
zabbix-web-pgsql.noarch : Zabbix web frontend for PostgreSQL
zabbix-web-japanese.noarch : Japanese font settings for frontend

MySQLインストール

DBMS として MySQL を使用するため、以下のようにインストールします。

# yum install -y mysql-server

以下を /etc/my.cnf に記述

[mysqld]

# 日本語設定
default-character-set=utf8
skip-character-set-client-handshake

innodb_file_per_table
innodb_buffer_pool_size=64M
innodb_log_file_size=16M
innodb_log_files_in_group=2

DBの準備

MySQLがインストールできたら、Zabbix用のDBとそのDBにアクセスするためのユーザを作成します。

サービス起動
# service mysqld start
# chkconfig mysqld on

MySQLに接続
# mysql -uroot

Zabbix用のDBの作成とユーザの作成
mysql> create database zabbix character set utf8;
mysql> grant all privileges on zabbix.* to zabbix@localhost identified by 'zabbixpassword';
mysql> flush privileges;

mysql> exit
Bye

NTPの設定

時刻がずれているといろいろ問題が出るのでNTPで時刻を同期しておきます。

インストール
# yum install ntp

サービス起動
# service ntpd start
# chkconfig ntpd on

Zabbix をインストール

以下のように yum コマンドでインストールします。

# yum install -y zabbix zabbix-server zabbix-server-mysql

初期データインポート

DBに初期データを投入する必要があるので、以下のように初期データを投入します。

# cd /usr/share/doc/zabbix-server-mysql-3.0.13/
# gunzip create.sql.gz
# cat create.sql | mysql -uzabbix -p zabbix
(パスワードを聞かれるので`zabbixpassword`を入力)

Zabbixの起動設定

Zabbixサーバの設定ファイル/etc/zabbix/zabbix_server.conf を開いて以下のように記述します。

DBName=zabbix
DBUser=zabbix
DBPassword=zabbixpassword
DBSocket=/var/lib/mysql/mysql.sock

上記設定ファイルを保存したらサービスを起動します。

# service zabbix-server start
# chkconfig zabbix-server on

Webインターフェースのインストール

このままだとGUIによる操作ができないので、Webインターフェースをインストールします。
Webインターフェースを動かすにはCentOS6標準のyumリポジトリのPHPのバージョンは古いので、SCL有効化します。

# yum install -y centos-release-scl

SCLを有効化したらPHP5.5のインストール

# yum install -y php55-php php55-php-bcmath php55-php-gd php55-php-ldap php55-php-mbstring php55-php-xml php55-php-mysqlnd

SCLはインストールしただけでは有効化されないので、以下のようにログインシェルの起動時のスクリプトでPHP5.5を有効化します。

#!/bin/bash
source scl_source enable php55

上記内容を保存したら、一旦ログアウトし再度ログインしPHP 5.5系が有効になっていることを確認します。

php --version でバージョンが5.5.x系であることを確認

# php --version
PHP 5.5.21 (cli) (built: Aug 30 2016 13:51:02)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies

同様に、SCLからApacheをインストールします。

# yum install -y httpd24

とりあえず httpd を起動して確認

# service httpd24-httpd start

ブラウザから http://192.168.33.10/ にアクセスして welcom 画面が表示されることを確認。

ここまで完了したら、やっとZabbixのWebインターフェス及びその関連パッケージのインストールです。

# yum install -y zabbix-web zabbix-web-mysql zabbix-web-japanese

zabbix-web の設定ファイルを Apache に適用するため httpd/conf.d の下にコピー

# cp /usr/share/doc/zabbix-web-3.0.13/httpd24-example.conf /opt/rh/httpd24/root/etc/httpd/conf.d/zabbix.conf

コピーした後以下のようにtimezoneを編集しておきます。

php_value date.timezone Asia/Tokyo

ファイルの編集が完了したら Apache 再起動

# service httpd24-httpd restart
# chkconfig httpd24-httpd on

再度、ブラウザからhttp://192.168.33.10/zabbix/ にアクセスして「Welcome to Zabbix 3.x」の画面が表示されることを確認。

Zabbixの初期セットアップ

ここからは Web 画面に従って Zabbix の初期セットアップを進めます。
スクショを貼ると大量になるので、入力/選択項目のみ記載しておきます。

  • Welcome
    • [Next step]
  • Check of pre-requisites
    • すべて OK であることを確認
  • Configure DB connection
    • Database type : MySQL
    • Database host : localhost
    • Database port : 0 (default port)
    • Database name : zabbix
    • User : zabbix
    • password : zabbixpassword
  • Zabbix server details
    • Host : localhost
    • Port : 10051
    • Name : (空)
  • Pre-installation summary
    • 内容を確認して[Next step]をクリック
  • Install
    • Configuration file "/etc/zabbix/web/zabbix.conf.php" created. と表示されているのを確認

これで Zabbix の初期セットアップ完了です。

Zabbixへのログイン

デフォルトで作成される管理者は以下の通りです

  • ユーザ: Admin
  • パスワード: zabbix

上記でログインできたら、Zabbix Server のセットアップ完了です。

Zabbixエージェント(zabbix_agentd)のインストール

今回はZabbixサーバ自身を監視対象とするため、Zabbixサーバを構築した環境にZabbixエージェントも導入します。

# yum install -y zabbix zabbix-agent

yum パッケージのインストール完了後、/etc/zabbix/zabbix_agentd.conf の確認(デフォルトで以下の状態となっているはず)

# ZabbixサーバのIPアドレス
Server=127.0.0.1
# エージェントをインストールしたホスト名とIP
Hostname=Zabbix server
ServerActive=127.0.0.1

エージェントの起動と自動起動の設定もしておきます。

# service zabbix-agent start
# chkconfig zabbix-agent on

エージェントの設定は以上です。

動作確認

これで、Zabbix によって自身のサーバの情報が収集されているはずですので、実際にそれを確かめてみます。

以下のスクショではUIが日本語化されていますが、Zabbixをインストール直後は英語の状態です。
UIを日本語化するには、ユーザプロファイル(画面右上の人のアイコン)から言語を選択することで変更可能です。

まず、ZabbixサーバのWebUIにログインし、画面上部のメニューから[設定]-[ホスト]を選択しします。そうするとホスト一覧が表示されるので、一覧から “Zabbix Server” の先頭のチェックを付け一覧の下の[有効]ボタンをクリックします。

enter image description here

これで Zabbix Server 自身が監視対象となりました。監視によって取得された情報を参照するには、上部のメニューから[監視データ]-[概要]を選択。

enter image description here

さらに、画面右上の[タイプ]のドロップダウンリストから”データ”を選択します。

enter image description here

その結果、以下のように Zabbix Server に組み込んだ Agent が収集した情報が画面に表示されます。

enter image description here

実際に運用する場合は、この後導入されたミドルウエアに応じた監視設定などを組み込む必要がありますが、この記事ではここまでにしておきます。
詳しくは書籍やWebサイト等を参考に設定してみて下さい。

参考

この記事を書くにあたって以下の書籍及びWebサイトの情報が参考になりました。

Liferay TIPS

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

LiferayのTIPS(小技)を3つご紹介します。

前提

Liferay 6.2 CE GA2のTIPSとなります。

ログレベル変更

Liferay全体のログレベルを変更するには、portal-log4j-ext.xmlを作成して配置する必要があります。

まず、以下のディレクトリを作成します。
{LIFERAY_HOME}/tomcat-7.XXXXX/lib/ext/META-INF

次にそのディレクトリに以下のファイルを作成します。

portal-log4j-ext.xml

<?xml version="1.0"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <category name="com.liferay.portal">
        <priority value="ERROR" />
    </category>

    <category name="my.portlet.soccer">
        <priority value="DEBUG" />
    </category>

</log4j:configuration>

com.liferay.portal の priority の値を変更することで、ログレベルを変更することができます。
Tomcatを再起動すれば反映されます。

Forward does not existエラー

IPアドレスでLiferayにアクセスしている場合、 Forward does not exist というエラーが発生する場合があります。
これは、対象のIPアドレスへのリダイレクトが許可されていないためです。
設定ファイルに許可するIPアドレスを追加します。

{LIFERAY_HOME}/portal-setup-wizard.properties

redirect.url.security.mode=ip
redirect.url.domains.allowed=
redirect.url.ips.allowed=127.0.0.1,[許可したいIPアドレス]

Tomcatを再起動すれば反映されます。

データ移行

Liferayにアップした画像やドキュメントはデフォルトではサーバ上の {LIFERAY_HOME}/data/document_library に保存されます。
サーバを冗長化するなどして、クラスタリング構成を組む場合、このデータをDBに移行することができます。
この機能はLiferayの管理画面から使用できます。
※事前にLiferayのストアタイプをDBStoreに変更しておいてください。設定されているDBに対してデータが移行されます。

ブラウザでLiferayにログイン→システム管理→コントロールパネル→サーバ管理→データ移行→
 新しいリポジトリフックを選択してください。
 dl.store.impl:com.liferay.portlet.documentlibrary.store.DBStore
 [実行]

実行後、自動で再起動されます。

作ろう、HTTPサーバ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

エンジニアをやっていると色々なモノの気持ちにならないといけない。バイナリツリーの気持ちになったり、CPUの気持ちになったり、メモリの気持ちになったり……。あ、書いたコードが低レイヤーでどんな挙動をするのか考えるっていう意味だよ。

普段使っているHTTPサーバの気持ちはどうだろう? 実際に書いて見ればわかるんじゃないかな。というわけで書いてみよう、Rubyで。

いきなりコーディング

HTTP通信の基本は知ってるよね。クライアントはTCPで80番ポートに接続してリクエストを投げ、サーバからレスポンスを受ける。いわゆるリクエスト-レスポンス型プロトコルだ。

だからまずはTCPで80番ポートをlistenしてブラウザにリクエストを送ってみてもらおう。

require 'socket'
module ReWheel
  class Server
    def initialize
      @tcp_server = TCPServer.new 80
    end

    def listen
      loop do
        client = @tcp_server.accept
        request = Array.new
        while request_line = client.gets
          break if request_line.chomp.empty?
          request << request_line.chomp
        end
        puts request.join("\n")
        client.close
      end
    end
  end
end
ReWheel::Server.new.listen

sudo ruby server.rb

ここでsudoをつけて実行することに注意。実はsudoを外すとエラーが発生して動かない。理由はHTTPの80番など1023以下のポート番号はLinux上で特権ポートとされているため。プロセスがこれらのポート番号をバインド(束縛)するにはroot権限で実行されている必要がある。

バインドというのは、あるポート番号をあるプロセスが占有するということだ。つまり、既にあるプロセスがバインドしているポート番号は、送られて来る通信を他のプロセスが受け取ることは出来ない。もしも、既にプロセスがバインドされている番号を新たに起動するプロセスが利用しようとすればエラーが発生する。試してみたければ上のスクリプトを二重起動すればいい。

( ˘⊖˘)。o(待てよ、受け取った通信を他のポートにもコピペで渡すアプリケーションを作れば一つのポートを実質的に複数プロセスで共有出来るのでは?)

と思った初心者はかしこい。それはnginxなどが持つリバースプロキシと呼ばれる機能の考え方だ。この機能があるからWebサーバは負荷分散や機能分担のために80/443ポートで受け取ったリクエストを他のポート番号でlistenしている同一マシン内のunicornやpumaに処理させたり出来るってワケ。

ま、とにかくこのコードを実際に使ってみよう。

ブラウザから来るリクエスト

では、サーバを起動した状態でブラウザからアクセスしてみよう。標準出力にこんな表示が出来て来るはずだ。

GET / HTTP/1.1
Host: 203.XXX.XXX.XXX
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.8,en;q=0.6

改めて考えるとちょっと不思議だが問答無用でHTTP/1.1のリクエストが送られて来る。サーバがどんなプロトコルに対応しているかは不明な時点で決め打ちされてくるワケだ。

では例えばHTTP/2.0の通信はどうするかというと、最初のリクエスト自体はHTTP/1.1で送られ、ヘッダ内にHTTP/2.0に対応可能である旨が記述される。そして、サーバ側もHTTP/2.0で対応可能であった場合はプロトコルが変更されてHTTP/2.0の通信が始まるというワケ。

次は送られてきたこのリクエスト文について読み解いて行こう。

[構成]

HTTPの規格については当然、というより言うまでもなくRFCのドキュメントを読むのが最も正しい。RFCではリクエストやレスポンスの構文はABNFで定義されている。この前書いた記事「作ろう、ミニ言語」でBNFが登場したがその拡張版にあたる言語がABNFだ。BNFみたいに自己参照しなくても繰り返しが表現出来たり色々と便利。
(作ろう、ミニ言語 https://doruby.jp/users/yam/entries/mini_lang)

HTTP-message
  = start-line
   *( header-field CRLF ) CRLF
   [ message-body ]

(出典:RFC 7230 の総集的 ABNF
https://triple-underscore.github.io/RFC723X-ABNF-ja.html)

うん、日本語にしよう。

1行目 開始行
2〜N行 ヘッダー
N+1行目 区切りのための空白行
N+2行目 メッセージ本文(オプション)

この形式はリクエストもレスポンスも共通であり、どちらも最低限に必要なのは1行目の開始行だけだ。まずは開始行の構文について説明しよう。前述したリクエストのサンプルで例えるとこうだ。

GET / HTTP/1.1
{メソッド} {URI} {プロトコル}

区切りは半角スペース。難しいことはなし。ヘッダについても項目がたくさんあるというだけで構文は難しくない。

Accept-Encoding: gzip, deflate
{パラメータ名}: {値}

じゃ次はサーバ側でレスポンスをしてみよう。レスポンスの場合はリクエストと開始行の構文が違う。先にABNFの方で見てみよう。

status-line
= HTTP-version SP status-code SP reason-phrase CRLF

SPは半角スペース、つまり”{HTTPバージョン} {ステータスコード} {理由句} 改行コード”という形式になる。ちょっと当てはめてみよう、このサーバはごく単純にHTTP 0.9で実装しておくとして、正常にGetできるのでコードは200、理由句はOKだ。

HTTP/0.9 200 OK

OK! ところで書いていて疑問に想ったことが一つ。ステータスコードはどういうステータスなのかが定義されている。では、わざわざ理由を書かないといけない理由はなんだ? もしかして自由に書いてもいいのか?

RFCのABNFを読んでみるとreason-phrase = *( HTAB / SP / VCHAR / obs-text )obs-text = %x80-FF、としか書かれていない。つまり省略してもいいし何を書いてもいい。普段、我々が目にするあのNot Foundといったフレーズはあくまでも推奨される1案らしい。どころかクライアントソフトでは無視することが推奨されている(RFC 7230)とのこと。ふーん…。
(HTTP responseのstatus-line、reason-phraseの内容にどこまでこだわるか?:
http://blog.magnolia.tech/entry/2017/10/09/224148)

返事をしてみる

じゃあさっきのサーバスクリプトにレスポンスを返すように書き加えてみよう。

    def listen
      loop do
        client = @tcp_server.accept
        request = Array.new
        while request_line = client.gets
          break if request_line.chomp.empty?
          request << request_line.chomp
        end
        puts request.join("\n")
        response_root(client)
        client.close
      end
    end

    def response_root(client)
      client.puts <<-EOS
HTTP/0.9 200 OK
Content-Type: text/html

Hello, <b>neko</b>.
      EOS
    end

サーバを起動し直してブラウザで接続。猫に会えたかな。基本はこれだけだ。しかしGoogleChromeで接続するとプロトコルはH1.0扱いされるな…0.9は対応外なのか、あるいは足して2で割られているのかは不明。

さて、今までのところは全くリクエストの内容を解釈していない。リクエストもレスポンスも好き勝手に投げているだけだ。ドッジボール・プロトコル…。次はキャッチボールをしよう。要求されたURLに応じて異なるコンテンツを返す。Railsなんかのルーティングに相当する処理だ。

その前にリクエストをパースする必要があり、ABNFが定義されているしミニ言語のようにちゃんとlex&parseしても良いわけだけれど…今日はそこまでやる気がない。String#stripで乗り切る。

require 'socket'
module ReWheel
  class Server
    def initialize
      @tcp_server = TCPServer.new 80
    end

    def listen
      loop do
        client = @tcp_server.accept
        raw_request = Array.new
        while request_line = client.gets
          break if request_line.chomp.empty?
          raw_request << request_line.chomp
        end
        request = Request.create_from_raw_text raw_request.join("\n")
        response = route_and_response(request)
        client.puts response.to_text
        client.close
      end
    end

    def route_and_response(response)
      case response.url
      when '/'
        Response.new.tap do |res|
          res.code = '200'
          res.body = 'Im root.'
        end
      when '/neko'
        Response.new.tap do |res|
          res.code = '200'
          res.body = 'Im a neko.'
        end
      when '/coffee'
        Response.new.tap do |res|
          res.code = '418'
          res.body = 'Im a tea-pot.'
        end
      else
        Response.new.tap do |res|
          res.code = '404'
          res.body = 'Not Found'
        end
      end
    end
  end

  class Request # ヘッダは面倒なので省略
    attr_accessor :method, :protocol, :url
    def self.create_from_raw_text(text)
      request_line = text.split("\n").map(&:chomp)
      start_line = request_line[0]
      self.new.tap { |req| req.method, req.url, req.protocol = start_line.split(' ') }
    end
  end

  class Response # reason-phraseは不要らしいので省略
    attr_accessor :protocol, :code, :body
    def initialize
      @protocol = 'HTTP/1.0'
    end

    def to_text
      start_line = [@protocol, @code].join(' ')
      [start_line, '', @body].join("\n")
    end
  end
end

ReWheel::Server.new.listen

書けた、第三部完。せっかくステータス・コードが選べるのでtea potも実装しておいた。厳密に言えばHTTPプロトコルではない(下記リンク参照)けど、遊びのスクリプトで書かなかったらどこで書いていいのかわからないよ。
(https://tools.ietf.org/html/rfc2324)

発展の話

というわけで基本的なことが出来るようになった。これをHTTP/2.0対応にしたり、あるいは自分だけのHTTPプロトコルを設計したり、unixドメインソケットでRackアプリに接続してみたり色々と試してみよう。

作ろう、サンドボックス・サーバ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

TL;DR

Webエンジニアは自分が自由に弄れるサーバを持っておこう。初心者への推奨はIDCFの500円サーバだ。

サンドボックス・サーバのススメ

Webエンジニアが知っておきたいことはなんだろう? アプリケーションのプログラミング、サーバやネットワークにミドルウェアといったインフラ、セキュリティ、フロントエンドの技術、UI/UXデザイン、有り体に言うと全部だ。せいぜい優先度の差があるだけだ。

だから、やはり手持ちの自由に使えるサンドボックスのようなサーバが一つくらいは必要だ。業務では触らない技術を試しに使ってみることも出来るし、Botやバッチを作成して働かせておくことも出来る。何より自分でWebサービスを構築することが出来るしね。

DockerやVagrant/VBといった仮想環境があるから問題ない? ネットワークに接続されて稼働し続けるサーバと仮想環境で発生し得る問題はかなり違うし、何より実際にサーバとして動いていないとサービスを稼働させてられない。だから仮想環境だけでは不十分だ。

ところで今やってるのは保守業務? それとも新規開発?

もし前者ならまさに業務と一致しているワケだ。やらない理由なんかある? 気になるあの問題を解決する新しい方法を自分の環境で先に検証してみよう。それに普段から実稼働するサーバに触れているから気軽に挑戦することが出来るでしょ?

もし後者なら業務では経験できない稼働サーバに触れることが出来るというワケだ。やらない理由なんかある? 特に新卒なら「プログラムを書いてローカルで動いた」のと「サーバ上で安定して実際に稼働し続ける」のにどれくらいの差があるかを知っておこう。

どこで作る?

色々な選択肢があるから自分で調べて選んでも良い。が、ここでは新卒エンジニアなど初心者向けということを前提にIDCF Cloudの500円サーバを勧めておく。

理由は3つある。「安い」「Iaas」「定額」だ。逆に他の有力な選択肢を選ばなかったことから説明していこう。

[Amazon EC2]
いいよね。無料期間が長いし、他のプロダクトグループもたくさんあって出来ることの幅が非常に大きい。それにAWSの採用率は業務でも高いし、これからも高まっていくだろう。ただ、初心者向けかというとそうじゃない。

特に定額ではない、というところがちょっと勧めにくかった。「間違って重ったい処理を走らせてCPU使ってしまった…」「しばらく放っておいたら脆弱性をつかれてBTC掘られてた…」といったミスで数百万の請求書に焦るのはよく聞く話である。オモチャには間違ってもいい奴を選びたい。

[Heroku]
いいよね。何も考えなくてもデプロイするだけで動くなんて最高じゃないか。Webサービスをデプロイしたいだけならね。OSやネットワークのレベルで操作できないPaaSはサンドボックスの趣旨から外れる。

というわけで、定額のIaaSで最小構成が有料期間でも安い、という基準でIDCF 500円サーバが出て来る。人間、何故か電子書籍の漫画を思いつきで購入は出来てもサーバに500円の課金をするのに抵抗感が生じてしまうこともあるが振り切ろう。

あ、念のため注意しておくとIDCFも完全な定額ではない。ネットワークの転送量が月に3TB超えてしまうとそれ以上は従量課金される。ネットワークの転送量でそれほど金額が飛び抜けることもないだろうが……。ちなみにこの課金は外部へ転送した分だけが計算される。例えばIDCF内に設置したDBサーバ – アプリサーバ間の通信などは関係ない。

最低限のセキュリティ

ここでは基本的な注意だけを書いていく。後は自分で調べたり転んで怪我をすることで覚えてほしい。

とりあえず、SSHだ。最低限やっておくことはポート番号をデフォルトから変えておくこと。これをサボってデフォルト設定のままSSH接続を許可していると驚くほど頻繁に何者かがマシンへの攻撃を試してくる。

ついでパスワード認証もやめたほうが良い。基本は公開鍵による認証だ。特に理由がなければIPも制限して自宅などからだけ接続可能にしておこう。その他の細かい設定は調べれば定番設定が出て来るはずだ。

いくらオモチャでも侵入されると他のサーバへの攻撃で踏み台にされたり色々と困ったことになる。備えよう。

その他にはログインの記録/認証失敗ログも発生時にメール通知したり、あるいはログイン時に簡単に参照できるようにスクリプトを組んだりしておくと便利かもしれない。(lastコマンドとか/var/log/secure*をgrep/tailするだけで出来る)

慣れてきたらmuninやnagiosといった監視ツールも使ってみよう。もしくは自分で作ってもよい。業務では許されない車輪の再発明やオレオレツールの作成が好きなだけ出来る。そう、サンドボックスのサーバならね。

予防も必要だが早めに気づくことで傷を浅く済ませることが出来る。

何をすればいい?

なんでも。ブログサービスを構築して自分のブログを書いてもいいし、お気に入りのサイトを監視してSlackへ通知するBotを作ってもいいし、身内専用のgitサービスを稼働させてもいいし、どこでも接続できる開発環境として扱っても良い。

自分がサーバを持っている、ということが普段見落としている「やってみたいこと」を気づかせるんだ。ハンマーを持っていれば何だって釘に見える、と言うようにサーバを持てば何でもコードで解決できることに見えてくる。サーバを持とう。

puppeteer で Headless してみた

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

puppeteer は Node.js からGoogle ChromeをHeadless mode(ウィンドウを表示しない状態のChorome)を操作するためのライブラリです。Headless であることの利点は今どきのJavascriptゴリゴリのSPAなどのテストをクラウドのようなディスプレイが繋がっていない機器で実行できたりするところです。 今日明日と2回に渡って puppeteer の使い方を紹介したいと思います。今日のこの記事では puppeteer をインストールしてWebサイトにアクセスしてスクショを取るところまでをやってみせます。

環境

  • Ubuntu 16.04 LTS (Amazon LightSail)
  • Node.js v8.9.2 (後述の手順でインストールします)

nvm 入れて node.js インストール

nvmじゃなくても良いのですが、慣れた手順なので nvm を入れて nvm から node.js をインストールします。

$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | sh

<一旦ログアウトして再度ログイン>

(インストール可能なバージョンを確認)
$ nvm ls-remote

(とりあえずVer8系を入れる)
$ nvm install v8

(インストールされたバージョンを確認)
$ nvm ls

puppeteer を インストール

puppeteer 自体のインストールは以下のコマンドで完了します。 Chrome がインストールされていない場合は Chrome も一緒にインストールされるようです。(自分の環境では先に Chrome をインストール済みでした)

$ node i puppeteer

とりあえず動かしてみる

以下はこのサイトを訪問してスクショを取るサンプル

// sample.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://doruby.jp');
  await page.screenshot({path: 'doruby.png'});

  await browser.close();
})();

上記を保存して node sample.js のように実行すると、カレントディレクトリに doruby.png が作成されますが…

enter image description here

上記のように日本語が「豆腐」になってしまいました。
日本語を表示するためには日本語フォントが必要なのでインストールします。

日本語フォントをインストール

とりあえずIPAフォントを入れてみます

$ sudo apt-get install fonts-ipafont-gothic fonts-ipafont-mincho

以下のコマンドでIPAフォントがインストールされていることを確認

$ fc-list | grep IPA
/usr/share/fonts/opentype/ipafont-mincho/ipam.ttf: IPAMincho,IPA明朝:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf: IPAPGothic,IPA Pゴシック:style=Regular
/usr/share/fonts/opentype/ipafont-mincho/ipamp.ttf: IPAPMincho,IPA P明朝:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf: IPAGothic,IPAゴシック:style=Regular
/usr/share/fonts/truetype/fonts-japanese-mincho.ttf: IPAMincho,IPA明朝:style=Regular
/usr/share/fonts/truetype/fonts-japanese-gothic.ttf: IPAGothic,IPAゴシック:style=Regular

再度動かしてみる

日本語フォントが入ったのでこんどはちゃんと表示されるはず、先程と同様に node sample.js を実行してスクショを取ります。

enter image description here

今度はちゃんと日本語が表示されましたね。

さいごに

以上のように puppeteer を使うと手軽にスクリプトからchromeを操作することができることがわかったと思います。
明日は、実際のWebサイトにアクセスしてWebUIの操作したり情報そ取得する方法について書く予定です。

作ろう、ミニ言語

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

TL;DR

yaccライクなRuby製パーサジェネレータであるRaccを使ってLISPもどきのREPLを実装する。

作ってみよう、処理系

さて、プログラミングがよくわからない文系エンジニアの自分も新卒2週目となったので、簡単なコードなら書けるようになってきた。

最近はプログラミングの基礎をちょくちょく勉強している。(難しくてよくわからないけれど)。今日はその一貫として比較的ポピュラーな遊び、ミニ言語の処理系実装をしてみる。

処理系ってなんだ? 簡単にいうと、テキストとして書かれたプログラムを実行するためのプログラムだ。単に処理系といってもコードを読んで他言語に変換して実行するコンパイラとそのまま読んで実行するインタプリタがあるが、今回やるのは後者のインタプリタ。もっと言えばRubyのirbのような対話型評価環境(REPL)だ。

それじゃまずは言語の設計からやってみよう。とはいっても、今回は難しいことをしない。この言語に出来ることはとても簡単だ。

  • 四則演算
  • 変数の宣言と利用
  • 値の出力

以上のみ。制御文くんのことは諦めよう、実装すると記事の長さが3倍くらいに伸びる。文法はLispのS式を踏襲する。理由はS式のための処理系はとても簡単だから。この言語の構文を言語の構文を表現するための記述方法であるBNF(バッカス・ナウア記法)で書くとこんな感じ。

    <code>    ::= <code> <s_exp> | <s_exp>
    <s_exp>   ::= '(' <func> <params> ')' 
    <param>   ::= <s_exp> | <atom>
    <params>  ::= <params> <param> | <param>
    <func>    ::= <text> | <identifier>
    <atom>    ::= <number> | <text> | <identifier>

うん、シンプルだね。ちょっとだけBNFの読み方について説明しよう。::=の左側が構成を定義される記号、そして右側がその記号の構成という表記だ。「|」は「または(or)」という意味で構成は複数ありえるというワケ。左側に置かれ定義されていないnumberやtextは終端記号と呼ばれる、これ以上分割できない単位だ。

例えば一番上のcodeは「codeは、code + S式の連続か、あるいは一つのS式から構成される」という意味になる。codeが自己参照しててややこしいが、要するに「codeは1つ以上のS式から構成される」ということを言っている。

レシピ

さて、実際どうやって作るか? コード生成しなくていいのでコードを字句(token)へ分解する字句解析部(lexer)と、それによって作られる字句を意味を持つ構文として解釈する構文解析部(parser)の2つだけあればいい。

それぞれを行うために色々なツールがあるが、今回はRubyで書きたいのでRuby製のパーサージェネレータであるRaccで作る構文解析器とStringScannerによる字句解析で作ってみる。

パーサージェネレータというのはその名前の通り構文解析器(parser)を生成(generate)するプログラムだ。どういうことかというと、先に書いてきたようなBNFなどで記述した導出規則から構文解析器を生成してくれる。仕様からプログラムが出来るなんて夢のようだな。

Raccは構文の規則を記述したDSLコードを元に実行可能なRubyファイルを吐き出す仕組みになっている。今回書かないといけないのはこのDSLコードだけ。

コーディング

おっしゃ、早速やっていこう。その前に言語へ名前をつけよう。Raccで作るからRacoonという名前にしておく。

lexer

まずは字句解析しよう。Raccでは実装しなければならないメソッドが決まっており、まずparseメソッドへ文字列が渡されてくるので、それをトークンへ分割しなければならない。そしてparseメソッド内でdo_parseメソッドを呼び出して構文解析器を動かす。

構文解析器ではnext_tokenメソッドをnilが出るまで呼び出し続けて解析していく。そこで、ここでは分割したトークンを配列@qとして保持してnext_tokenが呼ばれた時にshiftメソッドで切り渡していく。

---- header
require 'strscan'
require 'pp'

attr_accessor :racoon

def parse(str)
  # StringScannerを使ってトークン単位に分割する
  scanner = StringScanner.new(str)
  @q = []
  until scanner.eos?
    scanner.scan(/#[^\n]*\n/) ? nil  : # 改行は無視
    scanner.scan(/\s+/)       ? nil  : # スペースも無視
    scanner.scan(/\d+/)   ? @q << [:DEC, scanner.matched] : # 連続した半角数字を10進法の数とする
    scanner.scan(/[\(|\)]/)       ? @q << [scanner.matched, scanner.matched] : # ()はS式の基本
    scanner.scan(/[a-zA-Z0-9\-\_\/]+/) ? @q << [:TEXT, scanner.matched] : # とりあえず半角英数のみ
    scanner.scan(/./)     ? @q << [:ID, scanner.matched] :  # 他の全ての文字列は識別子扱い。全角文字のことは諦めよう
    nil
  end
  #pp @q
  do_parse
end

def next_token
  @q.shift
end

じゃ、試しにトークンへ分割してみよう。

"(+ 1 2)"
=> [["(", "("], [:ID, "+"], [:DEC, "1"], [:DEC, "2"], [")", ")"]]

"(var seven (+ 3 4))"
=> [["(", "("], [:TEXT, "var"], [:TEXT, "seven"], ["(", "("], [:ID, "+"], [:DEC, "3"], [:DEC, "4"], [")", ")"], [")", ")"]]

良さそうだ。じゃ、次はこのトークンの集合を構文解析部へ渡して組み立ててもらおう。

parser

Raccに規則部分を記述する。これはBNFをほとんどそのまま書けば良い。少し細かい記述の違いがあるのでちょっと修正する。

class RacoonParser
rule
    start code
    code    : code s_exp
            | s_exp
    s_exp   : '(' func params ')' { result = @racoon.send(val[1], val[2]) }
    param   : s_exp
            | atom
    params  : params param { result = val[0..1] }
            | param
    func    : TEXT
            | ID
    atom    : DEC
            | TEXT { result = @racoon.vars[val[0]] if @racoon.vars[val[0]] }
            | ID
end

一部の右辺にブロックが記述してあるが、これは省略されているだけで全ての右辺について処理が存在する。デフォルトの挙動はおそらく{ result = val[0] }

Raccは構文を解釈できると構文木の末端から順にブロック内の処理を行う(raccはyaccと同じくLALRなので左側から)。そして、resultに格納された末端の処理結果は上位の処理にvalとして渡される。わかりにくい? 例文を追ってみよう。

(+ 1 (+ 2 3))というコードはまず1の部分を評価する。1は計算の必要がないのでそのまま1だ。次に(+ 2 3)が評価され、5へ簡約される。(+ 1 5)となり、最後にこのS式が評価されるので6が得られる。

s_exp : ‘(‘ func params ‘)’ { result = @racoon.send(val1, val[2]) }

この規則のブロックにあてはめてみよう。例えば(+ 2 3)result = @racoon.send('+', [2, 3]) => 5になるというワケ。

見ての通り、@racoon(環境相当のオブジェクト)には四則演算や出力命令、変数命令を行うためのメソッドを実装する必要がある。ここについては何も難しいことはないのでコードだけ。

class Racoon
  attr_accessor :vars

  def initialize
    @vars = Hash.new
  end
# 面倒なので四則演算をまとめて定義
  %w(+ - * /).each { |id| define_method(id) {|args| args.flatten.map(&:to_i).inject(id.to_sym)} }

# 変数宣言varはハッシュオブジェクトに{変数名: 値}という形で変数を保持する。
# スコープ? 大域変数しかないよ。
  def var(args)
    variable_name, value = args
    @vars[variable_name] = value
  end

  def display(args)
    puts "=> #{args}"
  end
end

仕上げ

対話的な部分はgetsを使ってloopを回すだけ。せっかくなので寿司でもつけよう。

---- footer
parser = RacoonParser.new
line_count = 0
loop do
  begin
    line_count += 1
    print "#{line_count.to_s.rjust(3,'0')} <U+1F363>  > "
    parser.parse(gets)
  rescue Racc::ParseError => e
    $stderr.puts e
  end
end

最後に今まで書いてきたソースコードを元にraccを使ってパーサーを生成してから実行する。

racc -g -o racoon_parser.rb racoon.y; ruby racoon_parser.rb;

enter image description here

たのしい!

ENVを扱うときに気を付けている2つのこと

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

自分がRubyでENVを使ったコードを書くときに気をつけていることは、「`ENV.[]` より `ENV.fetch` を使う」と「環境変数の値に関わるテストは`[]`メソッドをstubする」です。 以下でその理由について解説します。

ENV.[] ではなく ENV.fetch を使う

# こうではなく
ENV['hoge']

# こう書く
ENV.fetch('hoge')

理由は以下のとおり

  • ENV.[] 場合、環境変数が未定義だと nil になる
    • 環境変数の参照している箇所ではなく、その取得した値を利用する箇所で想定外の挙動となり原因を見つけるのに時間が取られる。
    • ENV['hoge']' || 'default' のようにnilとなるのを防ぐことはできるが、忘れる場合があり確実じゃない。
  • ENV.fetch の場合、環境変数が未定義だと例外が発生する
    • ENV.fetch の箇所で例外が発生するので環境の不備、あるいはコードの不備であることがすぐにわかる
    • ENV.fetch('hoge', 'default') のように第2引数を使えば環境変数が未定義の場合のデフォルト値が指定できる。

使い捨てのコードや自分しか触らないプライベートなコードであれば、ENV.[] でも良いですが、プロダクションなコードではfetchを使う方がメリットが大きいと考えます。

Rspec では ENV[] で直接値を書き換えず必ず stub する

# こうではなく
it "xxx" do
  # spec 実行前の値をバックアップして書き換え
  @env_backup = ENV['hoge']
  ENV['hoge'] = 'fuga'
end

after do
  # spec 実行後にもとの値に戻す
  ENV['hoge'] = @env_backup
end


# こう書く
before do
  allow(ENV).to receive(:[]).and_call_original
  allow(ENV).to receive(:[]).with('hoge').and_return('fuga')
end

理由は以下のとおり

  • 前者の場合、環境変数のスコープはグローバルなため影響がグローバルに波及する
    • after do ... end で元に戻すのを忘れた場合、タイミングによって spec が失敗する状況になり、その調査に時間が取られる。
    • 実際には試したことないが、specを並列に実行させた場合にも相互に影響が出る?
  • 後者の場合、影響範囲は実行中のspecの中だけで、値を戻し忘れるということも発生しない。

参考: RspecでENVをどうstubするのがよいのか – Qiita

まとめ

  • ENV.[] より ENV.fetch を使ってコードが動作するために環境変数の設定が必要であることをコードで明示しよう。
  • RspecではENV.[]メソッドをstubすることで影響範囲を限定しよう

“同じ文字列”==”同じ文字列” => false

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

TL;DR

表示上は全く同じように見える文字列同士が==演算子でfalseを返すことがある。一つの原因として考えられるのはUnicodeが合成文字という仕組みにより濁音/半濁音を別扱いしているというもの。Unicode、また君か? 解決方法はUnicode正規化をすること。例えばrailsのActive Supoortなら以下のように正規化できる。

ActiveSupport::Multibyte::Unicode.normalize(string, :kc)

現象と原因の特定

この現象に遭遇したのはxmlからパースした文字列をrspecで期待値通りかチェックしていた時のこと。このように表示上は同じように表示されるが==(eq)演算子ではfalse扱い。

str1
#=> "電話番号 : 入力データが不正です"
str2
#=> "電話番号 : 入力データが不正です"
str1 == str2
#=> false

そこで、まずはバイトサイズを確認すると一致しない。そこでエンコーディングの差を疑って文字列のエンコーディングを確認するがこれも一致する。

[str1.bytesize, str2.bytesize]
#=> [54, 45]

[str1.encoding, str2.encoding]

#=> [#<Encoding:UTF-8>, #<Encoding:UTF-8>]

では何が違うかというと前述したとおり、str1では濁音/半濁音の記号が分離されている。これはString#unpackで文字列をコードポイントに変換したり、String#charsで文字単位に分割するとわかる。

str1.unpack("U*")
#=> [38651, 35441, 30058, 21495, 32, 58, 32, 20837, 21147, 12486, 12441, 12540, 12479, 12363, 12441, 19981, 27491, 12390, 12441, 12377]
str2.unpack("U*")
#=> [38651, 35441, 30058, 21495, 32, 58, 32, 20837, 21147, 12487, 12540, 12479, 12364, 19981, 27491, 12391, 12377]

str1.chars
#=> ["電", "話", "番", "号", " ", ":", " ", "入", "力", "テ", "゙", "ー", "タ", "か", "゙", "不", "正", "て", "゙", "す"]
str2.chars
#=> ["電", "話", "番", "号", " ", ":", " ", "入", "力", "デ", "ー", "タ", "が", "不", "正", "で", "す"]

これにはUnicodeの合成文字という仕組みが関係している。合成文字/結合文字(combining character)は非結合文字の直後に結合文字を配置することで結合文字列(CCS)を作る仕組みのこと。簡単にいえば「が」という文字列を「か(非結合文字)」「<濁音記号>(結合文字)」という2つのコードポイントで表してもいいということだ。ややこしいのが、Unicodeは「が」という一つのコードポイントで表しても良い点で、つまり同じ文字を表すのに異なるデータの表現方法がある。

当然、バイト列が違うのでそれぞれの表現でバイト数さえ食い違う。このままでは色々な問題が発生するため、これらのバラバラな表現を統一するように正規化することが出来る。

str1.bytesize
#=> 54
ActiveSupport::Multibyte::Unicode.normalize(str1).bytesize
#=> 45
ActiveSupport::Multibyte::Unicode.normalize(str1) == str2
#=> true

ちなみにUnicode正規化にはNFD、NFCなど複数の形式があり、上記のコードでは省略しているが、normalizeメソッドは2番めの引数に正規化形式を指定することが出来る。ex. normalize(str, :nfc)

ところで色々な問題が発生する、と書いたが面白いのはセキュリティに関する問題を引き起こすこと。文字列の比較はセキュリティに深く関わるトピックであり、XSSを防ぐためのエスケープにせよ、パスワードの認証にせよ、文字列が正しく比較出来ることを前提にしている。その前提をいじれてしまうのなら色々なことが出来てしまう。

考えられる他の原因

表示上は同じだがデータは異なる、という文字列比較のややこしい問題は他にも色々ある。やはり文字列データは非常に複雑なデータ形式ということだろうか。こういった問題が不完全な抽象化から抜け出せる日はまだ遠い。主が怒り全地の言葉を乱し人を全地に散らされたために人々は当面の間は苦しむ。

  • 非最短形式
  • 多対一の変換
  • 大文字と小文字
  • エンコード情報の不一致
  • 7ビットエンコーディングの解釈

Vimでファイル保存時に自動でrubocopを実行する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Rubyのコードを変更したら忘れずに Rubycop でチェックしたい。というときに使えるTipsです。

前提

  • Rubocop がインストール済みでrubocopコマンドにpathが通っていること

準備:NeoBundle のインストール

以下のページを参考に Vim に NeoBundle を導入します。(今時のVimプラグイン管理は dein.vim らしいのですが、自分の環境はまだ NeoBundle です…)

既に NeoBundle がインストール済みの場合、次の手順に進みます。

NeoBundleの導入 – Qiita

Vim に syntastic をインストールする

syntastic は Vim 上でのシンタックスチェックしてくれるプラグインです。

~/.vimrc に以下の行を追加します。

" syntastic インストール
NeoBundle 'scrooloose/syntastic'

" syntastic から rubocop を実行する設定
let g:syntastic_mode_map = { 'mode': 'passive', 'active_filetypes': ['ruby'] }
let g:syntastic_ruby_checkers = ['rubocop']

上記を簡単に解説すると…

  • NeoBundle 〜
    このの行はプラグインを使用することを NeoBundle に対して知らせるためのもので、もしそのプラグインがインストールされていなければ、vim の起動時に GitHub のしかるべき場所からローカル(標準では ‘~/.vim/bundle` の下)に clone して、vimのプラグインとして自動的に読み込んでくれる。
  • let g:syntastic_* = 〜
    この行は syntastic プラグインからRubocopを呼び出すための設定

これらを設定して Ruby のソースコードを編集して保存すると Rubocop のチェックが走ります。

実行してみる

ためしに以下のようなコードを入力して保存してみます。

class Hoge
  def foo
    puts "hoge"
  end
end

実行結果は以下のようになります。
警告が出てる行に$>が表示されます。また、:Errors と打つとエラーの一覧が表示されます。
他にもいろいろ便利機能あるので詳しくは、syntasticの公式ページを見て頂くと良いと思います。

enter image description here

これで、ローカルでrubocop流し忘れて jenkins おじさんに怒られずに済みますね。

参考

Rubocopをsyntasticを使ってVimから自動実行する – Qiita

コミュニケーション技術②

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回はコミュニケーション技術の一つ「コーチング」について紹介します。

前回の記事:コミュニケーション技術

コーチング

コーチング(coaching)とは、人材開発の技法の一つ。対話によって相手の自己実現や目標達成を図る技術である。相手の話をよく聴き(傾聴)、感じたことを伝えて承認し、質問することで、自発的な行動を促すとするコミュニケーション技法である。

出展:Wikipedia

コーチングの3原則

①インタラクティブ(双方向性)

インタラクティブとは一方通行の会話ではなく、双方向でアイディアを出し合い、それについて対等な立場で話し合うことを指します。

インタラクティブを促進するのに役立つスキルは「質問」や「ペーシング」です。大事なことは否定しないこと。答えは必ず相手の中にあるはずだからです。

②オンゴーイング(現在進行形)

継続性ともいわれますが目標達成までの間のサポートのことを指します。

「インタラクティブ」によって生まれた気付きは相手の行動を変化させます。
一時的にモチベーションは上がりますが徐々に下がっていきます。
目標へ到達するためにはたった一回の気付きだけでは難しく、コーチがリマインドを行いサポートすることが必要です。

③テーラーメイド(個別対応)

テーラーメイドとは相手の特徴や思考、行動パターンに注目してそれぞれへの対応方法を変えながら関わっていくことです。

人はそれぞれ違います。
人間100人いたら100人分の行動があります。
もちろん中には似たような行動もあるでしょう。
テーラーメイドを実現するためには「タイプ分け」が有効といわれています。

以上が「コーチング」の3原則になります。

しかし最終的な目標は自分自身で答えを見つけることです。
コーチングにおいては「オートクライン」を意図的に繰り返します。
最後にオートクラインについて簡単に紹介します。

おまけ

オートクラインの仕組み

enter image description here
オートクラインとは、医学用語で「自己分泌」という意味です。
今回のコーチングに関連したお話では「自分の行動、特に自分で発した言葉が自分自身に作用すること」を指します。

新たなアイデアや自問自答による気付きを発見する手法として有効とされています。
そして何より重要なのは自分の言葉であるということ。
自分自身の説得に本人の言葉以上のものはないと思います。

まとめ

今回の記事は「コーチング」の3原則
①インタラクティブ(双方向性)
②オンゴーイング(現在進行形)
③テーラーメイド(個別対応)

以上の3点を紹介しました。

次回はタイプ分けの手法「DiSC理論」について紹介したいと思います。

CoCProxyでリクエストを飛ばさずにWebクライアントをテストする

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

外部のシステムとの連携をテストしたいけど、気軽にそのシステムのAPIが叩けないとかいうときに役立つTips

CoCProxyとは

簡単に言うと、普段は何もせずリクエストを右から左に受け渡すが、ローカルにそのリクエストのホスト名とパスに相当する場所にファイルを置くと、実際にリモートにリクエストしたフリしてローカルのファイルの内容をレスポンスとして返してくれるプロキシという感じです。

GitHubが流行る前に CodeRepos という公開リポジトリがありそこにアップロードされていた Ruby 製ツールでしたが、現在その派生版として nginx モジュール版と、今回紹介する Node.js 版があるようです。

インストール

node.js 版をインストールは以下のとおり。

$ npm install -g cocproxy

基本的な使い方

適当な場所にディレクトリを用意(たとえば coc_test とする)してそこに移動。

$ mkdir coc_test
$ cd coc_test

リクエスト対象となるホスト名と同じ名前でディレクトリを掘って、その配下に差し替えたいリソースに合わせてファイルを配置する。

$ mkdir www.google.co.jp

まずは普通にアクセスしてみる

$ curl http://www.google.co.jp/index.html
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head> ....

普通に Google から HTML ファイルが返ってくる。

次に、別のターミナルでCoCProxyを立ち上げ、、、、

$ cocproxy
Proxy stand by: http://localhost:8087
Used mock dir : /home/ec2-user/coc_test

今度はProxyを通してアクセスしてみる。

$ curl http://localhost:8087/http://www.google.co.jp/index.html
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head> ....

まだ差し替えるファイルを用意していないので、今度も普通に Google から HTML ファイルが返ってくる。

次は、cocproxy でファイルを差し替えてみる。
以下のように www.google.co.jp/index.html にファイルを用意する。

$ cat > www.google.co.jp/index.html <<EOF
<!doctype html><title>hello</title>Hello CoCProxy.
EOF

先ほどと同じようにproxy経由でリクエストを投げてみる

$ curl http://localhost:8087/http://www.google.co.jp/index.html
<!doctype html><title>hello</title>Hello CoCProxy.

今度は CoCProxy によってファイルの内容に差し替えられたレスポンスが返ってきたことがわかる

RubyのコードからCoCProxy経由でアクセスしてみる

今度は先程と同様の手順を Ruby のコードで確認します。

# test.rb
require 'net/http'

Net::HTTP.start('www.google.co.jp', 80, :ENV) do |http|
  response = http.get('/index.html')
  puts response.body
end

Net::HTTPのドキュメントによると、#new や #start メソッドの第3引数に :ENV を渡すと環境変数 http_proxy が設定されていればそのProxyを経由するとあります。

試しに、最初は環境変数なしで確認します。

$ ruby test.rb
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" ...

普通に Google のコンテンツが返ってきました。
今度は、環境変数http_proxyを設定してみます。

$ export http_proxy=http://localhost:8087
$ ruby test.rb
<!doctype html><title>hello</title>Hello CoCProxy.

今度はローカルで用意したファイルの中身が返ってきました。

CoCProxy をProxyとして利用することでリモートに手を加えることなくレスポンスを変更することができることがわかりました。

まとめ

  • CoCProxy を使えばクライアント側のコードを変更せずにProxyでファイルを差し替えることができる
  • RubyのコードからProxyを利用する場合、そのライブラリに応じた設定が必要な場合がある
    • 標準ライブラリ Net::HTTP の場合、.new, .start メソッドの第3引数に :ENV を設定しているとき http_proxy 環境変数に値を設定されている場合そのProxyを経由してアクセスする

APIの時間占有率を簡単に調べてみた

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

ツールやgemを入れずに調べてみた。

 なるほど、ボトルネックはAPIに掛かる時間だけではなく、APIが呼び出される回数にも依存するのか、といつしか知ってから数ヶ月。
 RailsのAPIのどれが一番時間を占有しているのか取り敢えず簡単に調べたいなー、と思って特に何のgemとかも入れずに調べてみました。

準備

 取り敢えず、そのAPIの時間占有率を調べる為のデータ型を作ってしまおう。

 コンソール上で

bundle exec rails g model ApiOccupancyData

 マイグレーションを書き込み。最低限必要なのは、コントローラ名、アクション名、それからAPIの消費秒数と総呼び出し回数でしょう。

class CreateApiOccupancyData < ActiveRecord::Migration
  def change
    create_table :api_occupancy_datas do |t|
      t.string :controller_name, comment: "コントローラ名"
      t.string :action_name,     comment: "アクション名"
      t.integer :average_time,     default: 0, comment: "API消費秒数(ms)"
      t.integer :total_call_count, default: 0, comment: "API総呼び出し回数"
      t.timestamps
    end
    add_index :api_time_occupied_datas, [:controller_name, :action_name], unique: true, name: "api_name_index"
  end
end

 それで、最後にrake db:migrateしてモデル作成。

計測

 取り敢えず、ログを弄る、ログデータから取ってくるとかするのは、モンキーパッチをする必要がありそうだし、深く調べなきゃいけないし、色々時間が掛かりそう。
 というわけで、簡単にコントローラ全体にbefore_action, after_actionを掛けてしまって、時間を取ってしまうことにしました。

class ApplicationController < ActionController::Base
  before_action :set_time
  after_action  :recognize_time

  def set_time
    @start_time = Time.now
  end

 def recognize_time
    #ms単位で差を計測
    time_cost = ((Time.now - @start_time) * 1000).to_i
    #パラメータにコントローラ名とアクション名は入っている
    controller_name = params[:controller]
    action_name = params[:action]
    record = ApiOccupancyData.find_or_create_by(controller_name: controller_name, action_name: action_name)
    #平均時間を上書き
    record.average_time = ((record.average_time * record.total_call_count) + time_cost) / (record.total_call_count + 1)
    #総呼び出し回数を更新
    record.total_call_count += 1
    record.save
  end
end

 後はRailsを色々動かしてみて、データが貯まるのを待ちましょう。

出力

Railsコンソール上であとは、

ApiOccupancyData.order("average_time * total_call_count DESC")

 とでも打てば時間占有率が高い順にデータが列挙されていきます。
 そこから、API単位で詳しく見ていって、どこで時間が掛かっているか等、調べていけます。
 後、chart.jsで可視化とかをするとより分かり易くなったり。

API重み(絶対)
API重み(相対)

yumパッケージの中身を見たい

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

ときどきyumが提供するファイルを覗いてみたい時ありますよね?そんな時に使えるTips

確認した環境

  • CentOS release 6.9 (Final)

手順

例えば、php55-php というパッケージがあってその中身を確認したいと仮定し、まず対象の rpm を yumdownloader {パッケージ名} によって手元にダウンロードします。

# yumdownloader php55-php

上記の結果カレントディレクトリにphp55-php-5.5.21-5.el6.x86_64.rpm がダウンロードされました。
次に、以下のように rpm -qlp {rpmファイル名} でその中身を表示します。

# rpm -qlp php55-php-5.5.21-5.el6.x86_64.rpm
/opt/rh/httpd24/root/etc/httpd/conf.d/php55-php.conf
/opt/rh/httpd24/root/etc/httpd/conf.modules.d/10-php55-php.conf
/opt/rh/httpd24/root/usr/lib64/httpd/modules/libphp55-php5.so
/opt/rh/httpd24/root/usr/share/httpd/icons/php55-php.gif
/opt/rh/php55/root/var/lib/php/session
/opt/rh/php55/root/var/lib/php/wsdlcache

パッケージの中身が見れました。簡単ですね。

ざっくり解説

参考までにrpmコマンドの引数 -qlp php55-php-5.5.21-5.el6.x86_64.rpm の意味をざっくりと解説

  • -q 検索モードを表す(--queryの省略形)
  • -p{パッケージファイル} 選択オプション: 指定されたパッケージファイルを対象とする
  • -l 検索オプション:検jkkkkkkk索により見つかったパッケージのファイルの一覧を出力する

詳しくは man rpm などを参照してください。

最近人気な記事