ホーム ブログ ページ 27

作ろう、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 などを参照してください。

競技プログラミングのすすめ

0

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

この記事は、競技プログラミングとは何か、その始め方について書いた記事です。

はじめに

こんにちは、最近めっきり寒くなってまいりました。
朝起きるのが辛くなってきたmotsukaです
今回は競技プログラミングについてお話しします。

競技プログラミングとは?

競技プログラミングとは、制限時間内に与えられた問題をプログラミングを使ってどれだけ早く正確に問いて競う競技です。俗にプログラミングコンテストとも言い、
出題者側はテストデータを配り、参加者はそのテストデータを読み込ませて
作ったプログラムからの実行結果とテストデータの実行結果が同じなら、そのソースコードを出題者に提出します。出題者が回答が正しいかどうかを判定し順位を決定します。

どんな問題があるの?

問題としては、簡単なものから数学的な知識を問うものまで幅広くあります。
例えば、「3の倍数の時はfizz、5の倍数の時はbuzz、15の倍数の時はfizzBuzzを出力してください」のような、いわゆるFizzBuzz問題的なものもありますし、動的計画法や深さ優先探索などのちょっとした数学の知識やアルゴリズムの知識が必要な問題もあります。ここでは紹介しきれませんので、下記で紹介するサイトなどで実際に解いてみてください。

どんなコンテスト、サイトがあるの?

TopCoder
URL:http://www.topcoder.com
世界最大規模の競技プログラミングコンテストです。
出題される問題は英語なので、英語に自信がある方は挑戦するといいと思います。私は英語ができなくて挫折しました。

AtCoder
URL:http://atcoder.jp
日本語の競技プログラミングコンテストサイトです。
毎週土日にコンテストを行っており、また初心者向けのコンテストや上級者向けのコンテストもやっているので初心者の方にもオススメのコンテストサイトです。週末のコンテストに参加すると、毎回解いた数に応じてレーティングが更新されます。

まとめ

これを機に、プログラミングコンテストに出場してみて、プログラミングを学ぶ一助になれば幸いです。
実力がついてきたと思えて来たら様々なオンラインコンテストを開いているサイトで様々な問題を解いてみてください

gitで詳しい変更履歴が見たい

0

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

git blame, git log を使ってちょっと踏み込んで変更履歴を見てみたい!そんな気持ちにgitは応えてくれます。 git blameとgit logの個人的によく使うオプションの紹介をします。

画像はいい画像がなかったのでgitのソースコードにgit blameしてみたものです。

特にblameするつもりはないのですが git blame が好きです。
blameという名前がよくないですが、だいたい本当にblameしたいときに出てくるのは自分の名前です。
コミットを追うことでコードの意図がわかりやすくなったり、書いた本人に質問や相談もしやすくなるのでバンバンやりましょう。

git blame

git blame は各行についての最終コミットの情報を表示してくれるコマンドです。

git blameの基本

基本的な使い方は git bleme <ファイル名> です。
これで指定した各行の最後のコミットについて、

  • コミットID最初8桁
  • コミットした人
  • コミットの時間
  • 今のその行

が表示されます。

こんな感じです。

df0ba296 (Hoge Hoge         2017-06-30 14:18:16 +0900  1) # coding: utf-8
df0ba296 (Hoge Hoge         2017-06-30 14:18:16 +0900  2) require 'slack'
df0ba296 (Hoge Hoge         2017-06-30 14:18:16 +0900  3) require 'uri'
c0bd872f (Fugaga Fuga         2017-06-30 14:18:16 +0900  4) require 'net/http'
df0ba296 (Hoge Hoge         2017-06-30 14:18:16 +0900  5)
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900  6) BOT_TOKEN = File.read("./bot_token").strip
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900  7)
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900  8) SHEETS_ID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
...

特定の行だけ見る

これだけでも便利ですが、長いファイルだと目的の部分までたどり着くのが大変ですよね。
そういうときのために指定した行のぶんだけを表示させるオプション -L があります。

たとえば5行目から8行目だけ見たいときは git blame -L 5,8 <ファイル名> のように使います。

df0ba296 (Hoge Hoge         2017-06-30 14:18:16 +0900 5)
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900 6) BOT_TOKEN = File.read("./bot_token").strip
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900 7)
00000000 (Not Committed Yet 2017-11-30 11:16:44 +0900 8) SHEETS_ID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

git log

皆さんお使いであろう git log にも、コミットと変更の履歴が詳しく見られるオプションがあります。

特定ファイルに関するコミットのみを表示する

git log <ファイル名> で指定したファイル上の変更があったコミットだけを表示できます。

差分を表示する

-p オプションを付けると git diff で見られるような差分をコミットの情報の下につけてくれます。
ファイルは指定してもしなくても使えます。
こんな感じです。

commit 6813ebb5a01cbbf32e2f397a4afb1683dd94df8d (HEAD -> feature/design)
Author: Hoge Hoge <hhoge@example.com>
Date:   Thu Nov 30 18:40:05 2017 +0900

    画像差し替え

diff --git a/Assets/Scenes/Prefabs/Textures/bg.png.meta b/Assets/Scenes/Prefabs/Textures/bg.png.meta
index 550ba269..45d17bc0 100644
--- a/Assets/Scenes/Prefabs/Textures/bg.png.meta
+++ b/Assets/Scenes/Prefabs/Textures/bg.png.meta
@@ -12,6 +12,8 @@ TextureImporter:
     linearTexture: 0
     fadeOut: 0
     borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
     mipMapFadeDistanceStart: 1
     mipMapFadeDistanceEnd: 3
   bumpmap:
...

行を指定する

-L オプションを付けるとファイルの行も指定できます。

たとえば git log -L 10,20:<ファイル名> でファイルの10-20行目について変更があったコミットだけを表示できます。
-p オプションを付けたときと同じように、そのオプションに関する差分も出してくれます。

まとめ

git blame も git log もどのコミットでどういう意図で追加・変更されたものなのか簡単に知れるとうれしいですね。
別に犯人探しのつもりはないので、名前に惑わされず平和で楽しいgit生活を送りましょう。

[参考]

SDキャラを可愛く描く②【装飾編】

0

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

SDキャラの装飾を可愛く描くためのポイントについてまとめました。

はじめに

nocoです。
前回に引き続きSDキャラを可愛く描くポイントについて考えまとめました。
今回は髪や衣装など装飾のデフォルメが主な内容となります。
体はもちろん、それを彩るパーツでキャラクターを可愛く見せてあげましょう。

装飾のデフォルメの基本

前回冒頭で使用した画像を用いてざっと説明いたします。
enter image description here
デフォルメはキャラクターの特徴的な要素を目立つように描くことが重要です。
このキャラクターの場合耳のような髪としっぽ、髪飾り、振袖が特徴です。
そういったパーツは通常の等身と比べ大きめの比率で描くことで個性が際立ちます。

また、細かいパーツは描写を簡略化すると印象がすっきり見えて可愛くなります。
体がデフォルメされている分衣装もある程度デフォルメしないとちぐはぐな印象になってしまいます。

それぞれのパーツの描き方

先ほどの画像では説明しきれなかった細部のデフォルメについてお話させていただきます。

①アクセサリーのデフォルメ

enter image description here

キャラクターの装飾としてよく使われるリボンを例に色々デフォルメしてみました。
パーツ一つのデフォルメでもさまざまな種類があります。
そのキャラに似合う、すっきり見えるデフォルメの度合いを選んであげましょう。
enter image description here
 

②髪のボリューム

enter image description here

毛を太くしたりハリをもたせたり、髪にボリュームのあるデフォルメをしてあげると可愛らしくなります。
特におでこのラインから飛び出す前髪、ここに少しボリュームを持たせてあげるだけでもかなり印象が変わるように感じられます。
 

③アホ毛を付ける

enter image description here

アホ毛は付けるだけでシルエットがとても可愛くなります。
オリジナルでも既存のキャラクターのデフォルメでも印象から大きく外れなければ付けてあげてもよいかもしれません。(特に個人的な意見ですが…)
アホ毛はキャラクターの感情表現にも使いやすく、SDキャラと相性が良いように思います。

おわりに

2回に分けてスーパーデフォルメについての記事を書かせていただきましたが、いかがだったでしょうか。
まだわたしもデフォルメについて勉強中の身ですが、今回の記事が少しでもイラスト制作のお役に立ちましたら幸いです。

簡易リグで綺麗なカメラの軌道を描く

0

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

はじめに

最近はカメラモーションを使った演出などを考える機会が増え、カッコいいカメラワークを模索しています。
というわけで、コンストレイントを使ってカットシーン制作などで必要になってくるカメラモーション用の簡単なリグを作ってみます。

コンストレイントって何だ?という説明については前回の記事で軽く触れておりますので割愛します。

カメラモーションは綺麗な軌跡を

カメラワークも想定したモーションにおいて、ダイナミックなシーンや注視点をより際立たせるために気をつけたいことがあります。

それが、
綺麗なモーション軌跡を描くことです。
見ていてカメラの動きに違和感を感じてしまうようだと、頑張って付けたお気に入りのキャラクターモーションや壮大な背景モデルが台無しです。

3DCGにおけるカメラの構造

MAYAでは、常にあるオブジェクトの方向を向かせるエイムコンストレントという便利な機能があります。
エイムコンストレントはキャラクターの視線や顔の方向を制御するために使われることがあるもので、カメラの注視点固定用にも使えます。

しかし、エイム対象とカメラだけを作成してそれらにキーを打ってカメラワークを作ろうとすると綺麗なモーション軌跡を作ることが難しくなってきます。

例えば背の高いオブジェクトを
下から上へオブジェクトを中心にその周りをぐるっと1周しながら近づいていくカメラワークを作るとします。
カメラの移動に直接キーを打って作ってみるとガタガタとした軌跡になりがちです。

画像1

また軌跡を綺麗にするためにはその分たくさんキーを打たなくてはならなかったり、
修正が出て1周ではなく2周するよう要望が出た際、作り直しにかかる時間が多いのと修正作業が大変なのとであまり賢いとは言えません。

グループノードでリグを作る

綺麗な軌跡を作りたい時はカメラに対してどのような動きをしてほしいかを考え、それに応じたリグを作ってあげることが無駄の出ない方法だと思います。
具体的な方法としてはカメラを適当なオブジェクトなどにコンストレインしその上に制御用のグループノード階層を作り、それらに対してキーを打っていくというやり方です。

画像2

画像の構造では最下層のcampointにカメラをポイントコンストレイントしてその上にある
cam_trsでローカルのカメラの移動を制御
cam_rttX、Yでグループノードの回転軸を中心としたカメラの移動を制御します。

今回は注視点を中心にしてカメラがY軸に下から上へ一周回転しながら近づいていく動きをしてほしいので
cam_trsでZ軸(中心に近づいていく軸)とY軸(上下に移動する軸)の移動にキーを打ち、一周回転はcam_rttYにてY軸回転値の開始フレームに0、最終フレームに360と数値を入力します。
こうすることでガタつきのない綺麗なモーション軌跡を描くことができるかと思います。(見えづらくて申し訳ないです)

画像3

キーを比較してみましょう。上がカメラの移動でつけたアニメーションカーブ、下がリグでつけたアニメーションカーブです。

画像4
カメラの移動で頑張ってつけたモーションはこんなにキーを打ってしまっているのに対し、
リグを使って付けたモーションでは開始フレームと最終フレームにしか打っていません。
2周にしてくれと要望が出た際はcam_rttYの最終フレームの値を360から720にするだけで済むので急な変更にも対応できます。

グループノードでは親の階層の動きが子の階層に継承されていくため 階層の順番 や 各ノードの中心軸 を慎重に考えながら作ることが注意点です。
また、rtt_X、Yのように回転をわざわざ分けている理由は一つのグループノードで3軸動かそうとするとジンバルを起こしてアニメートしづらくなってしまうためでもあります。

作り方は人それぞれですが場面に応じた作り方で作りやすいリグを考えて作る習慣を身につけると無駄な時間を費やす事にならずにすみます。

おわりに

今回の階層に分けたリグでアニメートする、という方法はとても初歩的なものですが、コンストレイントとリグを組み合わせて使っていくことでイメージ通りの動きを実現できるようになると思います。
筆者もたくさん試して応用力をあげたいところです。

sshの秘密鍵のパスフレーズを設定していたかどうか思い出せないとき

0

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

秘密鍵にパスフレーズを設定していたかどうか忘れた場合・またはパスフレーズが正しいか手元で試してみたい場合に使えるTips

以下のコマンドで確認する。

$ ssh-keygen -yf ~/.ssh/himitsu

パスフレーズが設定されていない場合は、秘密鍵を元に作成された公開鍵が出力される。

ssh-rsa AAAAB3NzaC1yc123xyz..... 

パスフレーズが設定されている場合は、パスフレーズの入力を求められ正しいパスフレーズが入力されれば同様に公開鍵が出力される。

Enter passphrase: (正しいパスフレーズを入力)
ssh-rsa AAAAB3NzaC1yc123xyz..... 

以下、man ssh-keygen からの抜粋。

    -y      This option will read a private OpenSSH format file and print an OpenSSH public key to stdout.

普通に手元の秘密鍵から公開鍵を作るのにも使える(それが本来の使いみち)

キャラクターの設定を管理する

0

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

度の高い項目を抜き出す

enter image description here
  • 一人称
  • 二人称
  • 三人称
  • その他、特殊な呼称
  • 口調
  • 性格の属性
  • 特技
  • 好きなもの
  • 苦手なこと
  • 嫌いなもの
  • 他キャラクターとの関係性

ほぼ必須となるのが上記の11項目でしょうか。
(特技と好きなもの、苦手なことと嫌いなものは纏めてしまっても良いかもしれないです。)
場合によっては増減しますが、これらを分かりやすくしておけば資料を見返すための時間はかなり短縮できます。
これに加えて、三行程度で簡潔にキャラクターの説明をメモしておくと、すぐにキャラクターを把握することが可能です。

忘れがちな裏設定などもメモ

enter image description here

全てのキャラクターに裏設定が存在するわけではないですが、書いているうちにどんどん追加で設定が足されていく……というのは多々あると思います。
そういった設定も逐一メモを書き残しておくと良いかもしれないですね。

終わりに

最初から設定の管理が出来ていれば、自分のド忘れだけでなく、他人に引き継ぐ場合にも役立ちます。
今までは設定をきちんと管理せずにやってしまっていたので、この記事は自分への戒めのために書きました。
後から管理しようとすると本当に大変なので、気を付けましょう。

Trelloで簡単タスク管理

0

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


タスク管理の方法は、紙に書き出す、付箋を使って管理する、タスク管理ツールを使うなど人それぞれだと思います。 今回は、私が普段から使っているタスク管理ツールを紹介します。

Trelloとは?

Trelloとは、Fog Creek Softwareが2011年に開発したウェブアプリケーションです。
基本的に無料で使うことができ、付箋を扱うのに近い感覚でタスク管理を行うことができます。
それでは、実際に画面を見てみましょう。
enter image description here
[Fig1. 初期画面]

何もない青い画面が出てきます。この画面を ボード と呼び、全ての付箋が集まる画面です。しかし、このままでは付箋を貼ることはできません。

画面の「リストを追加」からリストを追加しましょう。
enter image description here
[Fig2. リスト作成]

ここでは例として、[ToDo], [Doing], [Done]の3リストを作成しました。
1つのタスクを書いた付箋は、このリストの中に作成します。作成した付箋のことを、 カード と呼びます。
タスクには重要度や要件によってラベル(Fig2中、カードについている緑と黄色のタグ)を付けることができます。

カードはタスクの進行状況に合わせて動かすことができます。
enter image description here
[Fig3. カードを動かす]

これにより、タスクの進行度合いが視覚的に把握できます。

また、TrelloはスマートフォンやPC、タブレットといった様々な端末で使うことができます。
PCで使う際には、私はGoogle Chromeで利用することをお勧めします。
その理由は、便利なアドオンの存在です。

あわせて使うと便利なアドオン

Elegantt for Trelloについて

ガントチャートというものがあります。
これについては触れませんが、Elegantt for Trelloはカードからガントチャートを作成するというアドオンです。
画面を見てみましょう。
enter image description here
[Fig4. Elegantt for Trello]

ガントチャートが作成されています。

また、ガントチャートからタスクの進行度合いが分かります。
enter image description here
[Fig5. 進行度合い1]

「やること1」の進行度合いは0%となっています。タスクの進行に合わせてカードを右のリストに移動させることにより、進行度合いも増加します。

enter image description here
[Fig6. 進行度合い2]

逆に左のリストに戻せば、進行度合いは減少します。

Card Colors for Trelloについて

このアドオンを入れていない状態では、カードの色は白となっています(「やること2」、「やること3」のように)。
Card Colors for Trelloは、カードの色を一番最初につけたラベルの色、またはついているラベルの色を混ぜた色とすることができます。
これによって、カードの色から重要度、要件を判断することができます。より視覚的にタスクを管理できるということですね。

おわりに

Trelloは個人で使うも良し、チームで使うも良しの大変便利なツールです。
皆さんもガンガン使ってみましょう。

【cron × Gmail】バッチ実行エラーを手軽に監視しよう

0

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

今回のお話

こんにちは。いくたです。
宝塚歌劇公式サイトの更新を5分ごとに巡回して通知するボットを作りました。

このようにほぼ常時動いてるようなプログラムを組む時には、 何かしらエラーが発生した時に気づけるような仕組み が必要です。
今回はcronのメール送信機能を使って手軽にエラーの監視をしていきます。

cron以外にもスケジューラは色々とありますが、サーバーをわざわざ立てたりログローテーションを設定したりと面倒なことが多そうです。
そこでcronで使える お手軽 な運用・監視方法を考えました。

(ボットの紹介は今回の趣旨とは違いますが、よかったらフォローしてください。)
https://twitter.com/PolarChildren

このボットの大まかな仕組み

  1. サイトの更新履歴ページの一番上(最新)をNokogiriでスクレイピングして取得(スクレイピングについて詳しく知りたい方はこちらの記事もご覧ください)
  2. 前回の結果(テキストファイルに保存してある)と比べる
  3. 違いがあれば各所(twitter/slack/line)に通知する
  4. 新しい結果をテキストファイルで保存

この一連のプログラムを5分に1回動かしているのが cron です。

cronとは?

cronとはUnix系のOSで使われるシステムで、様々なコマンドを定時実行してくれます。
詳しい設定方法などはこちらの記事を参考にしました。

今回作ったボットのcron設定

$ crontab -l
# 実行時の出力内容をメールで送ってくれる(ボット専用のメールアドレスを取得)
MAILTO="test.mail.for.bot@mail.com"

# cronの書式
# 分 時 日 月 曜日 実行したいコマンド
# サイト更新監視バッチ(毎時間の0分から55分まで5分毎に実行)
0-55/5 * * * * /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'

/bin/bash -lc の -l はログインシェルと同じ環境で実行したい時使うオプションで、 -c は引数にとった文字列( ruby ~/scripts 〜 以下)をコマンドとして実行するオプションです。

MAILTO を使うことで実行時の出力をメールに転送することができます。

# こんな感じのメールが来る
=====
From: サーバー
To: test.mail.for.bot@mail.com
Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'
-----
[debug] -- START --
[debug] loaded setting file.
[debug] execute: takarazuka
[debug] result is OK.
[debug] {:title=>"花組公演『ポーの一族』ムービーページ 更新!", :link=>"https://kageki.hankyu.co.jp/revue/2018/ponoichizoku/movie.html"}
[debug] This site is updated.
[debug] tweeted
[debug][message]
宝塚歌劇公式ホームページが更新されました。
【花組公演『ポーの一族』ムービーページ 更新!】
https://kageki.hankyu.co.jp/revue/2018/ponoichizoku/movie.html
[debug] -- END --
=====

メールを使ってエラー通知

ボット専用のメールは普段見ないのでエラーがあっても気づけません。
そこでエラーのログが出力されたメールを、普段使っているメールアドレスに転送するようにしました。

例外エラーをキャッチする

まず、プログラム内でエラーをキャッチして [error] と出力させます。
後々メールで出力結果を送った時に固定文字’error’でエラーメールを振り分けます。

puts '[debug] -- START --'
begin
  # サイトの更新を監視して通知する UpdateCatcher のインスタンスを生成・実行
  UpdateCatcher.new.execute
rescue StandardError => error
  # 例外エラーをキャッチして出力する
  puts "[error] #{error}"
end
puts '[debug] -- END --'

メールのフィルタ・転送を設定する

Gmailにはメールを条件で分けて処理するフィルタ機能があります。
Gmailのフィルタルールの作成方法

このフィルタ機能を使って、本文中に’error’が含まれるメールだけを自分のメールアドレスに転送します。
これで実行時に何かエラーが発生しても、すぐに気づいて対処できます。

# こんな感じのメールが来る
=====
From: サーバー
To: test.mail.for.bot@mail.com
Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'
-----
[debug] -- START --
[debug] loaded setting file.
[debug] execute: takarazuka
[error] Failed to open TCP connection to kageki.hankyu.co.jp:443 (getaddrinfo: Name or service not known)
[debug] -- END --
=====

メリット

メリット① 容量を気にしなくていい

RubyのLoggerを使えば割と簡単にログを出すこともできます。
しかし、サーバーにログを残すということは、多少なりとも容量を気にして管理することが必要です。

そこでログを出さずに全てメールで送ることでサーバーの容量を気にせず使えます。
(代わりにメールサーバーのディスク容量を圧迫していくので、自分のメールアドレスに全ログ送るのはやめましょう)

メリット② 必要な情報が検索しやすい

過去の実行状況を確認したいときも、送信日時(実行日時)やキーワードでメールを検索すればまとめて確認することができます。
個人的にはテキストファイルのログよりブラウザで確認できるほうが分かりやすくて気に入っています。

メリット③ Gmailのフィルター機能で応用が利く

Gmailのフィルター機能は細かくカスタマイズが可能です。
先ほどは転送機能を紹介しましたが、既読にしたりフラグを立てたりすることもできます。

Gmailのフィルタルールの作成方法

私の場合はエラーメール以外は既読にして、エラーメールはフラグをつけて後から探しやすくしています。

スクリーンショット 2017-11-29 13.34.41.png
エラーメールのフィルタ設定

デメリット

デメリット① 実行自体出来なかった場合気づけない

この仕組みはRubyが実行できる前提なので、なんらかの理由で実行ができなかった場合(ファイルが壊れてるとか)エラーを正しくキャッチできない場合があります。

# こんな感じのメールが来る
=====
From: サーバー
To: test.mail.for.bot@mail.com
Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb.hoge'
-----
ruby: No such file or directory -- /home/ikuta/scripts/takarazuka/update_catcher.rb.hoge (LoadError)
=====

これだと ‘error’の文字列に引っかからないので自分のメールアドレスには転送されません。
本来なら、cronで標準エラーを受け取ってメールする設定をする必要があります。
その場合、Rubyで例外エラーをキャッチすると異常終了ではなく正常終了するので、スクリプトの方も工夫が必要です。

参考:
cronの設定について
Unix :: cron / 標準エラー(STDERR)のみアラートメールする

デメリット② 頻繁にメールを送るとスパム認定されるかもしれない

今の所問題なくメールは送られていますが、昼も夜もなく5分に1回メールを送っているのでGmailに目をつけられてスパム認定されてしまう可能性もあります。

デメリット③ 汎用性がない

ログとしてファイルに出力すれば、そのデータからいろんなことを分析することができます。
今回のボットでいえば、「サイトの更新がどれだけ頻繁に起きているか」とか「何曜日が一番更新されやすい」とかです。
しかし、今回はメールで全て送ってしまって手元に何も残らないので、出力を眺めることしかできません。

まとめ

今回紹介した方法は業務レベルでは使えませんが、趣味でやっている分にはこれで充分かと思います。

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

rubyで抽象メソッドを表現する

0

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

rubyについて調べていたら抽象クラス/メソッドをサポートしていないようでした。
ですが抽象メソッドのようなものを簡単に表現できるようなのでそれを紹介したいと思います。


オーバーライドしなきゃエラー!


抽象メソッドはオーバーライドを強制するのが目的なので、オーバーライドされてなければエラーする形で実装してやれば表現できます。

class GameObject

  # GameObjectを継承した描画の実行はこれを呼び出す
  def draw
    draw_proc
  end

  # これを子クラスでオーバーライドしないと
  # drawから呼ばれてエラーになる
  def draw_proc
    raise "call abstract !"
  end

end


class Circle < GameObject
  def draw_proc
    puts "丸だよ"
  end
end


class Box < GameObject
  def draw_box
    puts "四角だよ"
  end
end
circle = Circle.new
circle.draw
=> 丸だよ

box = Box.new
box.draw
=> RuntimeError: call abstract !

draw_procを継承していないBoxクラスから描画メソッドを呼び出したらちゃんとエラーを出してくれました。


おわりに


rubyでプログラミングしていて「これエラーにならないんだ」と思うところが多々ありました。
それもそのはずで、そもそもrubyの設計思想がストレスなくプログラムを楽しめるようにといったものらしいのですが、c++を使っていた自分からするとある程度縛りの効いたルールがある方が安心して動けるので、この記事を書くに至ったわけです。
でもせっかく自由に楽しくがモットーの言語なのにそれを制限するのも変な感じがするので自由さを生かすような設計をしていきたいですね。

【roo】ExcelファイルをRailsで読み込む

0

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

今回は使っていて便利だった、excelを読み込むライブラリ【roo】について書きたいと思います。

はじめに

 約10年ぶりぐらいにバッティングセンターに行ったらバットにボールがほとんど掠りもせずに全身筋肉痛になりました、むらさきです。
 今回は、.xlsxファイルから必要な情報を抜き出して使うことがあった時に利用したrooというライブラリについてまとめたいと思います。

rooとは?

 rooとはスプレッドシートの読み込みができるライブラリです。.xlsや.xlsx、.odsや.csvなどに利用できます。google spreadsheetにも利用できるようです。
 読み込み専用で書き込みや新規作成等はできません。

インストール方法

 rooのインストールは

 gem install roo

 で簡単に行えます。

使ってみよう

インストールが終わったので実際に使ってみましょう。
今回は下のシートを使用します。rooは読み込み専用のライブラリなので、データを読み込んで表示させてみます。

enter the description

1.読み込みたいファイルを指定する

require 'roo'

xlsx = Roo::Excelx.new(file_path)

.xlsxファイルを取得する際には上記のように読み込みたいファイルのパスを指定して書きます。
.xlsxファイル以外の場合は

Roo::Excel.new(file_path)             #.xlsファイル
Roo::Csv.new(file_path)               #.csvファイル
Roo::OpenOffice.new(file_path)        #.odsファイル

と書く事で指定できます。

2.使用するsheetを指定する

xlsx.default_sheet = 'spices'
xlsx.default_sheet = xlsx.sheets[0]

直接sheet名を入力するか、sheetsの配列から指定するができます。

3.指定したセルに入っているデータを表示する
例として「ターメリック」と「シナモン」を取り出したいとします。
「ターメリック」はシートの2行、2列目
「シナモン」はシートの5行、2列目なので

xlsx.cell(2,2)   #=> "ターメリック"
xlsx.cell(5,2)   #=> "シナモン"

とすることで取得できます。

4.ループで取得する
最後に、例として材料の名前だけをすべて表示します。

2.upto(xlsx.last_row) do |row|  
  p xlsx.cell(row, 2)    #2列目を1行ずつ取得する
end  #=> "ターメリック","ナツメグ","唐辛子","シナモン","クミン","コリアンダー"

おわりに

いかがだったでしょうか。読み込みだけですがとても簡単に扱える便利なライブラリでした。
rooという名前だったのでシートにカレーのスパイスを使ったんですがスパイスを調べる際にセルフ飯テロをかましました。カレー食べたい。

最近人気な記事