ホーム ブログ ページ 25

Virtualboxを使用しAndroid 7.1をパソコンで動かす

0

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

PCで動作するAndroid-x86を使用して、Windows上にテスト用のAndroid仮想マシンを作成する手順です。 アプリなど実機と動作が異なったり、動かない場合もありますが、テスト用には使用できるかと思います。

実施環境

  • Windows 10
  • Virtualbox 5.2.4
  • Android-x86 64bit 7.1 RC2

ダウンロード

今回は、まだ7.1の安定版がリリースされていないので、RC2を使用します。
Android-x86から、android-x86_64-7.1-rc2.isoをダウンロードします。

仮想マシンの作成

ここからVirtualboxの仮想マシンを作成していきます。

仮想マシンの作成
名前は適当に、タイプはLinuxにしてバージョンはOther Linux (64-bit)にした。
メモリーサイズは、2048MBに増やした。

仮想ハードディスクの作成
ファイルサイズを、64GBに増やした。
作成ボタンを押す。

ストレージの選択
仮想マシンを選び設定を押す。
ストレージの光学ドライブに、先ほどダウンロードしたisoイメージファイルを設定する。
その他、お好みで設定を変える。
仮想マシンを起動します。

インストール

ブート画面
仮想マシンが起動したら、Installationを選択。

パーティション選択1
Create/Modify partitionsを選択し、パーティション作成していきます。

GPT
GPT使用しないので、Noを選択。

パーティション作成1
Newを選択し新規作成。

パーティション作成2
Primaryを選択。

パーティション作成3
全領域使用するので、そのままエンター。

パーティション作成4
Bootableにする。

パーティション作成5
Writeを選択。

パーティション作成6
yesと入力し、パーティション作成中しばし待つ。

パーティション作成7
Quitでパーティション作成画面から抜ける。

パーティション選択2
OKを選択して、先ほど作成したパーティションにインストールします。

ファイルシステム選択
ext4を選択。

フォーマット確認
Yesを選択し、フォーマット中しばし待つ。

GRUB
Yesを選択して、GRUBをインストールします。

システムファイル読み書き選択
どちらでもいいが、今回は実機に近いNoを選択した。

強制排出
マウントしたisoイメージを強制排出した。

インストール成功
Rebootを選択して、再起動します。

起動後の設定など

Android起動
Virtualboxのメニューから入力、マウス統合を外します。
画面の指示通り進めます。

メイン画面
完了
お好みで、Google日本語入力などを入れましょう。

ACPIシャットダウン
終了時は、VirtualboxのメニューからACPIシャットダウンで行います。

終了時選択
電源を切るを選択し終了します。

解像度を変更する方法

Virtualboxを使用しているAndroidの解像度を変更します。
debug mode
起動後、デバッグモードを選択します。

enter image description here
# mount -o remount,rw /mnt
# vi /mnt/grub/menu.lst

上記コマンドを入力して、ファイルを編集します。
kernelの行の最後に、vga=ask を追加します。
別のメニューとして項目を作りたい場合は、既存の行を参考にして追加してもよいでしょう。

解像度選択画面
再起動を、先ほど作成したメニューを選び、解像度選択画面に入ります。
リストの中から、好みの解像度を選択してください。

おわりに

WindowsPCでAndroid
開発環境を入れずにアプリの動作確認を行いたい場合などに、利用してみてはいかがでしょうか。
また、Android-x86はPCにも導入できるので試してみるのもいいかもしれません。

関連記事など

脆弱性診断サービスなど

弊社、アピリッツではセキュリティ診断サービスを行っております。
下記リンクから内容の確認と、問い合わせが可能です。

http://appirits.com/service/security.html

第7回「来年に流行りそうなゲームは何か?」

0

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

第7回「来年に流行りそうなゲームは何か?」について話していきたいと思います。

今年も後わずか!こんにちは、Lionです。
前回の記事いかがだったでしょうか?
本場のゲームプランナーが「どういう業務を行っているのか?」というのが少しでも伝わりましたら、嬉しいです!
さて、本日は「第7回 来年に流行りそうなゲームは何か?」について話させていただきます。

VRコンテンツのゲームが流行る?

私的にはVRゲームが今後流行ってくると思います。
Oculus RiftやVIVEなどの一般の人でもVRゲームを開発できるようなキットや一般家庭でVRを手軽に楽しめるPSVRなどが去年くらいから世間的に浸透しつつあります。
自分もVRゲームを開発(趣味で)しているのですが、ここ1~2年でVRゲームのクオリティは大幅に上がってきていると思います。
その中でも、自分が遊んでみて特に面白かったものをいくつか紹介いたします。

THE PLAYROOM VR

1人でも家族みんなでも楽しめるVRパーティーゲームです。
このゲームはVRヘッドセットを装着した人だけでなく、周りのみんなもテレビモニターのゲーム画面を見ながら一緒にゲームを楽しむことができるためPSVRが1個でも遊べるという利点があります。

対戦はもちろん協力プレイもあり、直感的な操作が可能なゲームばかりのため、小さい子供からお年寄りまで幅広く遊べます。
また、PSVRを装着していないプレイヤーの声が装着している人の手助けとなるゲームもあるので、みんなでワイワイ遊ぶのにはもってこいのゲームだと思います。

特にTHE PLAYROOM VRのMONSTER ESCAPEというゲームが面白いですね!
VRヘッドセットを装着したプレイヤーは、巨大モンスターとなって街をどんどん破壊していきコントローラーを持ったプレイヤーたちはヒーローとなってモンスターから逃げたり戦ったりします。
VRヘッドセットにはモンスターの視点による景色が映し出されるというのが戦略のカギになります。
enter image description here
        ©2017 Sony Interactive Entertainment Inc. All Rights Reserved.
                [sonyの公式サイトより引用]

MATIOKART VR ARCADE GP

こちらは東京都新宿区歌舞伎町1-29-1にある超現実エンターテインメントEXPO 「VR ZONE SHINJUKU」で遊ぶことのできるVRマリオカートのゲームになります。
このゲームの面白いところは、カーレース中に取得するアイテムを自分の手で取らなければいけないというのとそのアイテムを使う時に自分の手で使わなければならないところです
   enter image description here
       ©Nintendo Licensed by Nintendo、©BANDAI NAMCO Entertainment Inc.
             [超現実エンターテインメントEXPOより引用]
このように手にコントローラーを付けて、ゲーム内のアイテムを自らの手で取得します。
ゲーム内だと、以下のような感じ見えますね
       enter image description here
      ©Nintendo Licensed by Nintendo、©BANDAI NAMCO Entertainment Inc.
            [超現実エンターテインメントEXPOより引用]
ミドリこうらを持ってますね。これを相手に向かって投げるのがすごく楽しいです。
ただ、運転をしながらアイテムを投げるというのはなかなか難しかったですね(/・ω・)/
※ちなみにアイテムにハンマーがあるのですが、相手に近寄ってあてるのは結構難易度が高いです。
現状いままでやったVRゲームで一番楽しかったのはマリオカートVRだったので、興味ある方はぜひ遊びに行ってみてください!

ホラー実体験室 脱出病棟Ω

これは今まで自分がやってきたホラーVRで一番怖かったゲームになります。
病院の中を車いすで仲間と共に移動して、脱出するというゲームなのです。あまりにも表現がリアルすぎて、初めはコントローラーを操作するのもためらってしまいました。
       enter image description here
             ©BANDAI NAMCO Entertainment Inc.
           [超現実エンターテインメントEXPOより引用]

VRゲームは、ホラーとの相性はバツグンというのを象徴したかのように怖いです。
※ちなみに女性の大半は泣いているのを目にしました…

基本的に難しい操作はないです。その分、没入感が半端ない作り込まれた世界に集中することができます。
もし、興味ある方は超現実エンターテインメントEXPO 「VR ZONE SHINJUKU」にあるので遊びに行ってみてください。

ここまで話してみて

どうだったでしょうか?実はVRゲームって結構身近で遊べるようになってきたのが分かりますね!
また、人気のゲームタイトルのVRゲームも少しづつですが出始めていますし、VR専用のゲームセンターもできてきました。
私的には、来年からVRゲームが一気に流行りだすと思っています。
もしかしたら、私の好きなゼルダの伝説のVRゲームも出るかもしれませんね!
※というか出てほしい…自分でマスターソードを台座から抜きたいw

さて、次回は「第8回 必勝?!筆者独自の就活術」を話していきたいと思います。
今年ももうわずか(´・ω・`)みなさんもよいお年をお迎えください(/・ω・)/

EC2再起動でステータスチェック失敗した状態からリカバリした方法

0

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

EC2再起動でステータスチェック失敗した状態からリカバリした方法

EC2の再起動を実施した所、予想外のトラブルが発生し、なんとかリカバリした時の話。

何が起こったか

AWSのEC2でメンテナンスの為インスタンス再起動が予定された為、事前に手動再起動を実施。
(通常であれば難なく起動し、イベントから当該の予定が消えるはず。)
しかし、イベントから予定は消え、ステータスはrunningになったものの、インスタンスのステータスチェックに失敗。
よって、当該インスタンスにSSHもできなくなった。

試した事1 (失敗)

インスタンスの「再起動」ではなく「停止」&「起動」をしてみた。

結果

相変わらずインスタンスのステータスチェックに失敗

試した事2 (失敗)

当該インスタンスのAMIを取得し、そのAMIから起動を試みた。

結果

AMIから起動してもやはりインスタンスのステータスチェックに失敗

試した事3 (失敗)

AWSのトラブルシューティングに20分以上待つと解消するかもしれない、とあったので、
暫く待ってみた。

結果

1時間待っても解消せず。

試した事4 (成功)

当該インスタンスは、2台構成で冗長化してあるサーバの片方であった為、
もう片方のAMIを取得し、そのAMIから起動してみた。

結果

問題なく起動できた。

この段階での問題点

不運な事に、冗長構成のプログラムとは別に、(起動不能となった)当該インスタンスだけに設定してあったプログラムがあり、
そのプログラムが実行できない状態になった。

試した事5 (成功)

AMIより起動した新規EC2に、起動不能となったEC2のEBSをアタッチしてみた。

結果

問題なくアタッチできた。

あとは、マウントの設定と、いくつかプログラムの設定を実施し、なんとか平常状態にする事ができた。

MySQL の auto_increment をリセットする

0

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

MySQL の主キーとかに auto_increment 属性を付けておくと連番の ID を自動生成してくれるので便利です。

データ挿入時に id を指定せずデータ挿入すればよくなり、ユニークな ID が自動で割り当てられます。

> insert into names( name ) values( 'ヤマダ' );
> insert into names( name ) values( 'スズキ' );
> insert into names( name ) values( 'サトウ' );
> select * from names;
+----+------+
| id | name |
+----+------+
|  1 |ヤマダ|
|  2 |スズキ|
|  3 |サトウ|
     :
+----+------+

便利な auto_increment 属性ですが、コントロールする必要が出た場合に少しコツが必要になります。

3レコード目が誤って登録した事に気づいて、削除をした場合に
次にデータを挿入するとauto_increment はリセットされずに id = 4 として 3 件目のデータが挿入されます。

> select * from names;
+----+------+
| id | name |
+----+------+
|  1 |ヤマダ|
|  2 |スズキ|
|  4 |タナカ|
     :
+----+------+

これでも問題無いと考えるのであれば特に気にする必要ないですが、連番ではなくなってしまうので、
auto_increment を自分の意図でリセットしたくなる場合があります。

その場合は、以下のコマンドを実行します

alter table names auto_increment = 3;

auto_increment 属性値を強制的に「3」に変更しましたので、この状態からデータ挿入すると
次に入るレコードの ID は 「3」 になります:

> select * from names;
+----+------+
| id | name |
+----+------+
|  1 |ヤマダ|
|  2 |スズキ|
|  3 |タナカ|
     :
+----+------+

まとめ

基本的には自分のローカル環境でしか行わない作業ですね。
特に気にならなければ行わない処理なので、連番に出来る方法もあるんだなー
程度に覚えておけば良さそうです。

手軽な運動で運動不足を解消

0

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

最近運動不足なのをひしひしと感じているので、手軽に出来る運動は無いか調べてまとめてみました。
以下のサイトを主に参考にしています。

公益財団法人 健康・体力づくり事業財団

ウォーキング

一日につき一万歩歩くと、生活習慣病の予防に良いとのことです。
一万歩というのがどれくらいの距離なのかイメージがつきにくかったので、計算してみました。
歩幅の目安については、明確な値は出てこなかったのですが、タニタのからだカルテというページを参考に計算してみます
タニタ からだカルテ

歩幅の目安(諸説有り):身長 × 0.45
・男性,平均身長の場合
 171(cm) × 0.45 × 10,000(歩) = 7.695 (km)
・女性,平均身長の場合
 158(cm) × 0.45 × 10,000(歩) = 7.11 (km)

大体一日あたり7km〜7.5kmくらい歩くと良いみたいです。
分散して歩いても良いようなので、階段の昇り降り等を増やして少しでも多く歩くよう心がけると良いかもしれません。
効果的な歩き方のフォームなども掲載されているので、興味の有る方は一度参考ページを御覧ください。

ストレッチ

簡単なストレッチでも、運動不足の身体には効果的だそうです。

肩のストレッチ

準備運動で行う、片方の腕をもう片方の腕で引き寄せる運動です。
10秒間、2セットずつ行うことが推奨されています。
enter image description here

伸び

息を吐きながら、思い切り伸びを10回ほどするのも良いそうです。
これならデスクでも簡単に出来ます。
enter image description here
イラストだと手を組んで伸びをしていますが、握りこぶしを作って伸びたほうがよりストレッチには良いとのことです。

比較的やりやすそうな2つを取り上げました。他にもストレッチ方法は紹介されていますが、簡単なものでも健康に良いときいて驚きました。

ランニング

以前手頃な運動について調べていた時に、ランニングについて良く目にしたので合わせて紹介したいと思います。

手頃なスポーツをしてみる

同じく財団のページには書いてありませんが、手頃なスポーツをしてみるのも運動不足や気分転換に良いそうです。

お金や時間はかかってしまいますが、スポーツジムを利用してみるのも一つの手かもしれません。

明治神宮球場には、バッティングセンターもあるので、そういったところで手軽に楽しんでみるのも良いですね。
Doruby内で紹介している記事もあるので、是非御覧ください。

やきうのすすめ

まとめ

公益財団法人 健康・体力づくり事業財団のページを参考に、いくつか簡単な運動について紹介しました。
こういった簡単なものから取り組んで、運動不足を解消していきたいと思います。

Redmine3.4.3にredmine_backlogsを入れた時の備忘録

0

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

新しいバージョンと古いプラグインはキノコとタケノコくらいに相容れない

 Redmine3.4.3にプロジェクト管理ツールであるBacklogのRedmine用である、redmine_backlogsを入れてみたのですが、その時に色々と詰まったので書き残しておきます。

動かせるようになるまでの過程

1

まず、普通にredmine_backlogsをRedmineのプラグインとしてgitから落としてきて、

bundle exec rake redmine:plugins:migrate RAILS_ENV=production

を行ったらエラーが発生した。
そこから色々手を加えたりして、頑張ったりしたけれど簡単な解決があった。
Redmine3.2にredmine_backlogsを入れた人のブログがあったので、それを参考に。
・ソースは本ブランチのものではなく、origin/feature/redmine3 のものを使用。
・nokogiriとcapybaraをGemfileからコメントアウトする。
でok。後はマイグレーションして再起動すれば反映される。

2

次に、チケットを作成しようとした時に、
plugins/redmine_backlogs/lib/backlogs_hooks.rb

return unless project.module_enabled?('backlogs')

で、projectがnilだと怒られてエラー発生。
try当て込んだら取り敢えず動いた。

return unless project.try(:module_enabled?, 'backlogs')

そこから詳しく調べると、
app/views/issues/new.html.erbで呼ばれていて、それは要するにチケットを作成しようとした時(issues/newが呼ばれた時)の、app/controllers/issues_controller.rbのnewメソッド
に問題がある。
それからprojectを入れているのは、newのbefore_actionの中でのbuild_new_issue_from_paramsメソッドの

@issue.project = @project

それから、@projectを入れているのは、app/controllers/application_controller.rbのfind_optional_projectの、

@project = Project.find(params[:project_id]) unless params[:project_id].blank?

要するに、パラメータにproject_idが入っていない場合、@projectが空っぽのまま処理が進んでエラーが起きる、という事。
ただ、どうしてここのproject_idが空っぽなのか? 存在していないプロジェクトに対してチケットを作成しようとした? そもそもトップ画面からのメニューで既にproject_idが入っていなかったからエラーが起きただろうけど、他の部分は動かせていたのは何故……?
エラー箇所を元に戻してみてもう一度エラーを起こしてみようと思いましたが、その時にはまた何故か正常に動くようになっていて、原因は闇の中に消えてしまいました……。
ログも削除済みでした……。

3

redmine_backlogs/lib/backlogs_setup.rb:234行目でエラー。

def settings
  SettingProxy.instance.to_h
end

これだと、文字列=>値のハッシュが作成されるが、これを参照している箇所ではキーを文字列ではなくシンボルで参照しているので、そのように変更する。
with_indifferent_accessメソッドを使うか、手作業で変えるかはお好みで。

4

それから、javascript関連でもエラー。このredmine_backlogsを動かす為に一緒に導入したa_common_libというプラグインにてエラー発生。
plugins/a_common_libs/assets/javascripts/a_common_lib.js:25にてbuildFilterRowが定義されていないと怒られる。
えーっと、これはどこのものだ……? と調べた結果、redmine本体のpublic/javascripts/application.jsに存在している事が判明。どういう理由か分からないけれど、このファイルがプラグイン以下で読み込まれていないんだな……。
色々と調べてみると、
app/application_helper.rbのjavascript_include_tagsで、pluginに入っているjavascriptファイルは全て取ってきているらしいけれど、public以下のソースを取ってきている様子はどうも見当たらない。
前のバージョンでは、public以下のソースを取ってきていたんだろうか……と思う。
で、取り敢えずbuildFilterRow、 それからenableValuesの部分をコピペしてa_common_lib.jsに移植する。それが一番外部に影響が少ないだろうし……。
……動いた!
取り敢えず、これで特に問題はなくなりました。

+A

後、他のプラグインを入れた時にそのbacklog関連でエラーが発生したりしましたが、取り敢えず、backlog関連のデータが入っているものを無視するようにすれば、問題はなくなりました。

Redmineを触ってみての感想

Railsに触れ始めてもう8ヶ月ほどが経ちましたが、それでもいきなり全く知らないコードを読んでの修正はとても大変でした。
エラーが起こればコードを追いかけてひたすら原因部分を見つけ出し、応急処置をするように直してを繰り返し……。これ以外のプラグインも入れたりもして、エラーがそれぞれで起きたりもして、それぞれ直す事もしましたが、このredmine_backlogsが一番手間が掛かりました。
本格的に直そうと思ったら、Redmineにもかなりの時間を割かなければいけないでしょうし、どれだけ時間が掛かるか正直分かりません。
でも、正常に動くプラグインに関しては本当に数コマンドで新しい機能が使えるようになって、とても使い易いなー、とも思いました。

……疲れた。太ってきたけど甘いものが欲しい。

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することで影響範囲を限定しよう

最近人気な記事