embulk-filter-eval というフィルタープラグイン書いた
イントロ
まぁ半分ネタも入ってますが、何かちょっとした事を行う程度なら役に立つはず。
これは何が便利?
input
として与えられたカラムに対して、Ruby のコードでちょっとした変換を加える事ができる embulk プラグインです。
embulk
の version 0.5.2
で作りました。
example
github の README に書いてあるサンプルそのままなのですが。(サボってるわけじゃなくて、何かサンプルがあったほうが説明しやすい)
以下のように embulk
コマンドを使って sample を作り、preview できたとします。
$ embulk example $ embulk guess embulk-example/example.yml -o config.yml
preview
すると、以下の様な出力です。
$ embulk preview config.yml +---------+--------------+-------------------------+-------------------------+----------------------------+ | id:long | account:long | time:timestamp | purchase:timestamp | comment:string | +---------+--------------+-------------------------+-------------------------+----------------------------+ | 1 | 32,864 | 2015-01-27 19:23:49 UTC | 2015-01-27 00:00:00 UTC | embulk | | 2 | 14,824 | 2015-01-27 19:01:23 UTC | 2015-01-27 00:00:00 UTC | embulk jruby | | 3 | 27,559 | 2015-01-28 02:20:02 UTC | 2015-01-28 00:00:00 UTC | Embulk "csv" parser plugin | | 4 | 11,270 | 2015-01-29 11:54:36 UTC | 2015-01-29 00:00:00 UTC | NULL | +---------+--------------+-------------------------+-------------------------+----------------------------+
eval_columns
では、例えば全ての id に 1
を足して出力したい、という要望があったとします。
こんな時に filter-eval
では簡単に書くことが出来ます。 config.yml
に以下のように指定するだけ。
filters: - type: eval eval_columns: - id: value + 1
value
には id 列の要素が入ってきます。
なので value + 1
とすると、最初は value =1 なので、 value + 1
が評価されて 2 が返って、次は 3 が返って... というわけです。
$ embulk preview config.yml +---------+--------------+-------------------------+-------------------------+----------------------------+ | id:long | account:long | time:timestamp | purchase:timestamp | comment:string | +---------+--------------+-------------------------+-------------------------+----------------------------+ | 2 | 32,864 | 2015-01-27 19:23:49 UTC | 2015-01-27 00:00:00 UTC | embulk | | 3 | 14,824 | 2015-01-27 19:01:23 UTC | 2015-01-27 00:00:00 UTC | embulk jruby | | 4 | 27,559 | 2015-01-28 02:20:02 UTC | 2015-01-28 00:00:00 UTC | Embulk "csv" parser plugin | | 5 | 11,270 | 2015-01-29 11:54:36 UTC | 2015-01-29 00:00:00 UTC | NULL | +---------+--------------+-------------------------+-------------------------+----------------------------+
実行してみたら、id のカラムが 2, 3, 4, 5
となりました。
out_columns
このプラグインでは出力したいカラムを制御することも可能で、out_columns
という設定で、出力したいカラムを配列で書けば可能です。
filters: - type: eval eval_columns: - id: value + 1 out_columns: - id
これは out_columns
に id
しか指定していないので
$ embulk preview config.yml +---------+ | id:long | +---------+ | 2 | | 3 | | 4 | | 5 | +---------+
こんな感じになります。
まとめ
またゴミのような gem を作ってしまった...
次はもっといいものを考えてます。
rbenv の plugins
イントロ
今使ってる rbenv
の plugin
たち。
rbenv
はこういうエコシステムが便利だから使い続けている。
最近務めている会社では流行りのようなので、私もやってみる。
plugins
% l .rbenv/plugins/ rbenv-ctags rbenv-default-gems rbenv-gemset rbenv-update ruby-build
順に紹介
rbenv-ctags
ruby のコアライブラリにタグファイル(ctags
用)を作ってくれるやつ。
ruby のコアライブラリを追うときに便利。だが、これは vim 用のタグしか吐いてくれない。(作者が vim 使いだから?)
vim 使える人は本家を使えばいいのだけど、私は emacs
しか使えないので、emacs
用のタグを吐くように、fork したものを使ってる。
PR 出すと vim マッチョな作者に殺されそうなのでしていない。
rbenv-default-gems
sstephenson/rbenv-default-gems
rbenv build
を使って ruby を install した時に、自動で gem を install してくれるやつ。
一回入れてしまうと便利。有名。
少し変わったことといえば、mgi166/dotfiles で default-gems
ファイルをバージョン管理していて
rake install default-gems
で ~/.rbenv/default-gems
ディレクトリにシンボリックリンクを貼ってくれるようにしたが、このメソッド、全く流行る気がしない。
% cat ~/.rbenv/default-gems bundler pry pry-byebug pry-stack_explorer ripper-tags
デフォルトの環境汚したくないけど、汚しまくっていて、羞恥心がなくなりつつある今日この頃。
rbenv-gemset
一言で言うと rbenv
版 rvm gemset
gem を管理するやつ。bundler
はプロジェクト単位で管理するけど、これは自在に namespace を作ることができる。
昔は gem 作るたびに gemset
作ってた。Gemfile
作るのめんどいけど、何か作ろうかなーって時に使う。
最近は何かやる際には gem
にするつもりがなくても bundle gem
や bundle init
で頑張る場合が多く、出番が少ない。
あ、でも rails
っていう gemset 作っておくと、rails new
する時楽で好き。
ruby-build
のように、.rbenv-gemsets
ファイルの有無で gemset
が決まるので、bundler
と共存する道はある、と思ってる。
% rbenv gemset list 1.9.3-p545: chef_repo veewee-boxes 2.1.0: d_parallel hipchat_searcher 2.1.2: dangerous_open_uri hipchat_searcher qiita-notify rails s3_utils 2.1.5: rails 2.2.1: rails 2.3.0-dev: rails
rbenv-update
rbenv update
コマンドで、全ての plugin を git pull
してくれるやつ。
これのお陰で ruby の新しいバージョンが出るたびに、毎回
cd ~/.rbenv/plugins/ruby-build
git pull
しなくてよくなった。 コマンド覚えやすいから好き。
ruby-build
rbenv-build
じゃなくて、ruby-build
rbenv
を有名にした MVP
。(と思っている)
説明不要。
まとめ
ネタになると思ったけど、有名どころしか使ってない。
boot2docker 内のコンテナに ssh で接続する
イントロ
最近ようやく docker を触り始めています。
にわか丸出しなので、【翻訳】Dockerコンテナ内でSSHDを実行してはいけない理由 | POSTD のような記事を見かけたのにも関わらず、ssh で接続しようとしています。
理由は serverspec
使ってコンテナのテストしたいと考えたからです。
(ssh
接続しないとできない... はず)
方法
Dockerfile
FROM ubuntu:14.04 RUN apt-get update RUN apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo 'root:root' | chpasswd RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config EXPOSE 22 CMD /usr/sbin/sshd -D
build
% docker build -t sshd . % docker port $(docker run -d -P openssh) 22 #=> 0.0.0.0:49155 % docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5d215294c5cf sshd:latest "/bin/sh -c '/usr/sb 17 seconds ago Up 16 seconds 0.0.0.0:49155->22/tcp boring_hodgkin
無事コンテナが作れた。
docker run
に -P
オプションを渡さないと expose
された port が公開されないらしく、若干ハマった。(https://docs.docker.com/reference/run/#expose-incoming-ports)
docker port
コマンド実行結果の port 部分を覚えておく。
接続してみる
-p
の引数は先ほどメモった docker port
コマンド実行結果を渡す。
% ssh root@$(boot2docker ip) -p 49155 The authenticity of host '[192.168.59.103]:49155 ([192.168.59.103]:49155)' can't be established. RSA key fingerprint is 6c:52:de:f7:85:8f:29:69:8c:5c:78:d4:ec:43:3d:69. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[192.168.59.103]:49155' (RSA) to the list of known hosts. root@192.168.59.103's password: Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.2.0-76-generic x86_64) * Documentation: https://help.ubuntu.com/ The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@5d215294c5cf:~#
入れたー
おまけ
boot2docker
で試した理由は、docker
周辺の仕組みを理解したかったからであって、boot2docker
でないといけない理由はありません。
後、観測範囲では、何故か boot2docker
で試した例が見当たらなかったから。
ActiveSupport::Configurable の話
イントロ
よく設定ファイルで見るクラスを簡単に作れる module ActiveSupport::Configurable
の紹介
使い方は、rails/configurable.rb at master · rails/rails のコメント文を読んだほうが早い。
よくあるやつ
具体例を幾つか紹介。
引数が違ったり、ブロック引数有り/無しだったり、メソッド名が違う等の差はあれど、どれも似ている
vagrant
Vagrantfile を拝借
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "centos7" config.vm.network "private_network", ip: "192.168.33.10" .... end
rails
config/environments/development.rb
を拝借
Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false ... end
tapp
esminc/tapp より
Tapp.configure do |config| config.default_printer = :awesome_print config.report_caller = true end
rails_config
RailsConfig.setup do |config| config.const_name = 'AppSettings' config.use_env = true end
他にもあったけど、紙面の関係上省略。
使ってみる
インスタンスを使う
require 'active_support/configurable' class MyConfig include ActiveSupport::Configurable config_accessor :id, :name end c = MyConfig.new c.id = 'aaa' c.id #=> 'aaa' c.name = 'bbb' c.name #=> 'bbb' c.hoge #=> NoMethodError: undefined method `hoge'
config_accessor
でメソッド名を渡すと、(内部でゴニョゴニョしてるが)アクセサが作られるというだけ。
また、config
というインスタンスメソッドが定義されて、それを呼ぶと登録した設定が全て取れる。
c.config
#=> {:id=>"aaa", :name=>"bbb"}
クラスを使う
ActiveSupport::Configurable
を include したクラスでは、ブロックを渡すお馴染みのインターフェイスが提供される。
class MyConfig include ActiveSupport::Configurable config_accessor :id, :name end # みたことあるやつや! MyConfig.configure do |config| config.id = 'aaa' config.name = 'bbb' end MyConfig.id #=> 'aaa' MyConfig.name #=> "bbb" MyConfig.config #=> {:id=>"aaa", :name=>"bbb"}
とはいえ、これだとクラス自体が設定を持っていて、何か気持ち悪い。
応用
例えば以下のようにクラスを書いてみると、しっくりくる。
class MyApp class Config include ActiveSupport::Configurable config_accessor :id, :name end def self.configure(&block) yield config end def self.config @config ||= Config.new end end MyApp.configure do |config| config.id = 'aaa' config.name = 'bbb' end MyApp.config #=> <MyApp::Config:0x007f9b7b9ab870 @_config={:id=>"aaa", :name=>"bbb"}> config = MyApp.config config.id #=> 'aaa' config.name #=> 'bbb'
- 設定を保持するクラス(
MyApp::Config
)にActiveSupport::Configurable
を include させる- 更に
config_accessor
でアクセサを宣言する
- 更に
とするだけで
MyApp.config
とすれば設定インスタンスを参照可能- MyApp を参照できれば、誰でも
config
を知ることができる
- MyApp を参照できれば、誰でも
MyApp::Config
はアクセサを定義しているだけ
みたいな事ができ、馴染みのあるコードになってきた。
けどこれだけだと、config_acessor
の代わりに attr_accessor
でいいんじゃね?ってなる。
オプションを渡す
ところがどっこい config_accessor
にはオプションを渡すことができる。
instance_reader
と instance_writer
だ。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name, instance_reader: false end c = MyConfig.new c.id = 'aaa' c.id #=> "aaa" c.name = 'bbb' c.name #=> NoMethodError: undefined method `name'
instance_reader: false
を与えると、MyConfig#name
という reader のメソッドが提供されなくなるので、NoMethodError
になる。
instance_writer: false
はというと、その逆になる。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name, instance_writer: false end c = MyConfig.new c.id = 'aaa' c.id #=> "aaa" c.name = 'bbb' #=> NoMethodError: undefined method `name=' c.name #=> nil
今度は MyConfig#name=
というメソッドだけ提供されていないので、writer を呼ぼうとすると NoMethodError
になる。
ブロックを渡す
default 値を定義することもできて、ブロックを渡せば可能。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name do 'bbb' end end c = MyConfig.new c.name #=> "bbb"
まとめ
rails/configurable.rb at master · rails/rails
デフォルト値などを考えだすまでは attr_accessor
でいいんじゃないか...? って思いました。
けど使い方次第でしょうねー
React.js で親子関係にある component の更新順序
イントロ
話題の React.js
を去年末ぐらいから、ちょこちょこと触っています。
殆ど Javascript
触ったこと無い状態から、いきなり React
始めて大分修羅の道だったのですが、最近ようやく慣れてきました。
何となくわかってきた React の性質っぽいものを残しておくメモ
これは何か
React.js
で作った component の更新順序を見てみた、というもの。
結論を言ってしまうと、親子関係にあると、必ず 「親 → 子」の順序で更新が行われる
現象
React.js
の基本原則として、this.setState
を呼ぶと、それがトリガーになって render
が呼ばれる、というものがある
(state
とは Ruby でいうインスタンス変数のようなもの。setState
は setter
だと思えばいい)
以下のように、子も親も同時に setState
を呼ぶとどうなるのだろう?という話。
超簡単なサンプルを作ってみた。
var Parent = React.createClass({ getInitialState: function() { return ( { age: 0 } ); }, setAge: function() { var value = this.refs.age.getDOMNode().value; this.setState({ age: Number(value) }); }, increment: function() { this.setState({ age: this.state.age + 1}); }, render: function() { console.log('in parent'); return ( <div id="parent"> <h2>parent</h2> <p>input parent age: {this.state.age}</p> <input type="text" ref="age"/> <button onClick={this.setAge}>submit</button> <h2>child</h2> <Child increment={this.increment} /> </div> ); } }); var Child = React.createClass({ getInitialState: function() { return ( { age: 0 } ); }, increment: function() { this.setState({ age: this.state.age + 1}); this.props.increment(); }, render: function() { console.log('in child'); return ( <div id="child"> <p>parent age: {this.state.age}</p> <button onClick={this.increment}>a year ago</button> </div> ); } }); React.render( <Parent />, document.getElementById('parent') );
最初に親の年齢を入力して、a year ago
ボタンを押すと、子供の年齢も親の年齢も 1 インクリメントするやつ。
ところでこのアプリ、親の年齢を入力しないと、親も子も 0 歳からスタートする。0 歳の親ってどういうこっちゃ。
けど細かいところは気にしない。
サンプルの重要なところは React の render で parent
から child
を作ってるところ。
つまり
- parent - child
のような構造を作っている。
一番下の a year ago
ボタンをクリックすると、console には
in parent in child
と出力される。
これは先に親の render
が呼ばれて、その後に子の render
が呼ばれることを意味している。
で、何がいいたい?
どうしてこういう順序になるのか、わからないです。
直感的には setState
が先に呼ばれた方、でも良い気もするので。
こういうもの、という理解でいいのだろうか?
「親→子」という流れは理解しやすんだけど、それで無理やり理解した気になるのもなんだかなー。
「理性の限界」を読んだ
イントロ
理性の限界――不可能性・不確定性・不完全性 (講談社現代新書)
- 作者: 高橋昌一郎
- 出版社/メーカー: 講談社
- 発売日: 2008/06/17
- メディア: 新書
- 購入: 56人 クリック: 299回
- この商品を含むブログ (176件) を見る
人間の「理性の限界」について、色々な角度からディスカッション形式で話が進んでいく。
途中「ハイゼンベルクの不確定性原理」や「ラプラスの悪魔」といった物理学の面白い話がでてきて、大学が物理学科の私は思わずニンマリ。
本を知ったきっかけ
part16後編のニコニコ市場を見たら、自分の持ってる本が入っててびっくりした。
「理性の限界――不可能性・不確定性・不完全性」は意外な発見があって面白いのでおススメ。特に、アローの不可能性定理は目からウロコでした。
http://t.co/UGvCN7ozKs
— なるしす (@kurodataro) 2014, 9月 14
最近ニコニコ動画の
3つのボタンでカービィボウル
という動画を見てしまった。
動画は勿論面白いんだけど、作者がすごい人だなと思っていて、彼がオススメするならって記憶に残っていた。
個人的な動機
読んでみようと思った理由はそれだけじゃない。
より合理的で、理性的に判断できるようになりたかったから、というのもある。
合理的な判断とはナンゾヤ、という話もあるけど、「今、自分は落ち着いていて、冷静に考えているか」ってこと。
常に意識したいことの一つです。
そういう基準や客観性をどうやって評価するか、維持するか、ということに興味があった。
背景
自分が「こっちのほうが良いんじゃないかなぁ」と考えることが、相手は「あっちのほうが良い」と思うことが、往々にしてある。
日常生活レベルの話なら「個性」と言い換えることができて、結構面白い。
けど合理的な判断を求められる仕事でも起こりえて、こちらは途端に厄介になる。
時に感情的な判断をしがちで、環境によっては到底理性的とは思えない判断をすることがある。不思議なことに。
でも仕事とは限らずに、自分もそういうことはあるし、他人を観察してそう思えることもある。
振り返ると「なんであんなことを」と思うけど、実際そういう時は客観性を失っていて「感情的になっていること」自体に気づかなかったり、あるいは気づかない振りをするものだと思う。
動機の動機
自分は冷静だと考えているけど、相手が感情的になっている場合。人によっては説得しようとすることが逆効果なこともある。
自分の説明が下手なのかな?と思ったけど、なんだか納得してくれているようには見える。でも相手の主張は変わらない。
そんな不思議な体験をした。
きっと自分が感情的になっている事に気づいていないからだろう、と思われる。
そんな時に「相手が自発的に自身の客観性を認識するためには、どうすればよいか」「自分は冷静になっているつもりで、実は論理的な説明ができていないのか?」という悩みを抱える事になった。
難しい。
本を読んで
「理性的とはナンゾヤ」という話にあまり触れず、相対的な議論が展開される。
「◯◯という方法は××という方法より、こういう理由で理性的だ」という感じ。
だから客観性をどうやって評価するか、という疑問の答えにはならなかったけど、ヒントにはなったし、単純に読み物として面白かった。
「神の非存在論」はなるほどなーという感じ。「神は理性では認識不可能」って結論は興味深い。
認識不可能だけど、神様という概念をほとんどの人間が理解していたり、或いは創りだしたりする。
神様 SUGOI
そんな神様を創れる人間はもっと SUGOI
「運と実力の間」を読んだ
イントロ
運と実力の間(あわい)―不完全情報ゲーム(人生・ビジネス・投資)の制し方―
- 作者: 木原直哉
- 出版社/メーカー: 飛鳥新社
- 発売日: 2013/06/08
- メディア: 単行本
- クリック: 2回
- この商品を含むブログ (6件) を見る
年末に読んだ本のうちの一つ。暇だったからブログに書いてみる
私の最近のマイブームが poker で、poker といえば木原直哉氏。
というわけで氏が書いた本を読んでみるか、と極めて安直な理由。
感想
面白い。今の自分が持ってない知見もあった。
内容は木原氏の生い立ちと WSOP 出場に至るまでの経緯、出場経験について語られていた。
元々興味を持って購入したので、その分楽しめた。
エンジニアリングとギャンブルについて
ここからは本の感想ではなく、極めて個人的な意見。
(観察対象があまりに少なすぎると思うけど) プログラムが書くことが得意なプログラマは(私を含む)、こういうギャンブル系が苦手な人が多いと思っている、という話。
言い換えると「確率で考え、実際に行動すること」が不得意な人が多い、ということ。
わかっていても行動できなかったり、そもそもそういう方面に興味が沸かない方が多い。
どうしてそう思うんだろうなーと今まで疑問だったけど、「運と実力の間」を読んでその理由っぽいものを思いついたので書き残します。
勝つまでのプロセスが違う
「勝つまでのプロセスが違う」というのがその有力候補。
当然「勝つ」とは何か、という定義は難しい。人によって違うから。
poker 側の「勝ち」は簡単に言うと「お金が増えること」
プログラム側は仮に、プログラムがどこかでエラーを吐いていて、それをデバッグし、直す作業を考えてみる。
直せたら「勝ち」としよう。直せなくて諦めたら「負け」
poker の話
本の中では以下のように述べられている。
- 負けている時にやり方を変えるな
曰く、負けている時にやり方を変えたら、何が原因で負けているか、わからなくなるからだ。と。
私の拙い経験としても賛成意見。
これは運が絡むゲームにはよくある考え方だと思う。
プログラミングの話
じゃあプログラミングではどうだろうか。
状況にもよるだろうけど
- デバッガを起動し、原因を突き止める
- 設定ファイルを見直す
- ログを追う
等が思い浮かぶ。
共通しているのは、プログラム(またはそれに関連する物)を少しだけ書き換えて、何度も実行する、ということ。
行った修正でエラーが出なくなるか、ちゃんと確かめないといけない。
どんなときも根気強く前に進み、心が折れるのが一番マズい。(経験上)
逆に言えば、プログラムを書き換えないで再実行する意味は無い。(時間に関連するバグなど例外はあるが)
何となく rspec をもう一回叩いてみるか... は時間の無駄だと思う。失敗する時は原因を潰さない限り、何度も失敗する。
だからどこに原因があるか色々考えたり、調べたりして、あらゆる方法を試すのだ。
何が言いたいかというと、負けている時には色々試すのが、プログラミングをする上で良いやり方だと思うってこと。
前提の話
プログラミングと poker では勝てるようになるまでのプロセスが違う、という意見なんだけど、もう一つ重要な考え方がある。
それは人は自身の向き不向きを自覚していて、自然に向いているものに興味を持つ、という考え方。方位磁石が自然と北を向くような感じ。
特に子供の頃はそのような傾向が強く見られるように思える。 これはもう一つ示唆するところがあって、それは「そう簡単に自分の向き不向きが変わらない」という事。
まとめの話
- プログラミングと poker は勝つ方法が違う
- 人は自分に合った興味を持つ
- しかもそれは中々変わらない
という意見から、プログラミングが好きな人はギャンブルに興味を持ちにくく、しかも不得意である可能性が高い。と思うようになった。まる。
言語化すると長い...
で、何が言いたい?
グダグダ書きましたが、全部極論です。
視野が狭いとこうなるので、気をつけねば。
株や投資などには、個人の自己責任でお楽しみください。
binding.pry 使ってる時に、一気にループを抜ける方法
イントロ
みんな大好き pry
の話
何回も binding.pry
が呼ばれる環境下で、毎回 exit
を入力するのめんどいよね、という話。
サンプル
class Test def aaa binding.pry 'aaa' end end
describe '#aaa' do it '1' do expect(test.new.aaa).to eq('1') end it '2' do expect(test.new.aaa).to eq('2') end end
この場合 binding.pry
は 2 回、処理を停止する。
4: def aaa => 5: binding.pry 6: 'aaa' 7: end
exit
叩く。
4: def aaa => 5: binding.pry 6: 'aaa' 7: end
もう一回 exit
叩く。
あるあるですね。
こういう時、一回でプロンプトまで戻りたいわけです。
解決法
!!!
or exit!
or exit-program
を入力する
4: def aaa => 5: binding.pry 6: 'aaa' 7: end >> !!! % # 即座に終了した!
知らなかった...
rails でカスタム generator 作る話
これは何か
好きな generator を作る話。ここで述べる generator
とは
rails generator hoge
のような rails generate
コマンドのこと。
知識整理のために書いてみる
雛形作成
rails には generator を生成する generator があって、以下の様なコマンドを叩けば良い。
rails generate generator <generator名>
試しに test
という generator を作ってみる。
% rails generate generator test create lib/generators/test create lib/generators/test/test_generator.rb create lib/generators/test/USAGE create lib/generators/test/templates invoke test_unit create test/lib/generators/test_generator_test.rb
ファイルが作られた。 作られた generator は以下のように実行できる
rails generate <generator名> (<引数1>, <引数2>, ...)
引数は generator 側の定義で制御する
generator 作成
生成されたファイル
先ほどの rails generate generator test
コマンドで生成されたファイルを順番に見ていく
lib/generators/test/test_generator.rb
class TestGenerator < Rails::Generators::NamedBase source_root File.expand_path('../templates', __FILE__) end
メインの generator ファイル。
Rails::Generators::NamedBase
を継承した {generator名}Generator
というクラスが生成されている。
ここにメソッドを定義していく。
具体例
TestGenerator
に何も生産しないメソッドを作る。
class TestGenerator < Rails::Generators::NamedBase source_root File.expand_path('../templates', __FILE__) def ore puts "俺が" end def dhh puts "DHH、こと" end def da puts "David Heinemeier Hanssonだ!" end end
rails generate
コマンドを叩くと TestGenerator
クラスのインスタンスメソッドが 上から全部実行される
% rails generate test test # test という引数を仮に与えている 俺が DHH、こと David Heinemeier Hanssonだ!
ご覧のとおり上から実行されていることがわかる。
なのでファイルを作りたかったら、そういうメソッドを書けばよい。
ディレクトリを掘りたかったら、そういうメソッドを書けば良い。簡単
lib/generators/test/USAGE
中身は以下の通り。
Description: Explain the generator Example: rails generate test Thing This will create: what/will/it/create
ヘルプコマンドの下の方にそのまま使われる
% rails generate test -h Usage: rails generate test NAME [options] Options: [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated applications) Runtime options: -f, [--force] # Overwrite files that already exist -p, [--pretend], [--no-pretend] # Run but do not make any changes -q, [--quiet], [--no-quiet] # Suppress status output -s, [--skip], [--no-skip] # Skip files that already exist Description: Explain the generator Example: rails generate test Thing This will create: what/will/it/create
lib/generators/test/templates/
ディレクトリ。現在は空。 erb テンプレートファイルを置いておく用のディレクトリ
test/lib/generators/test_generator_test.rb
require 'test_helper' require 'generators/test/test_generator' class TestGeneratorTest < Rails::Generators::TestCase tests TestGenerator destination Rails.root.join('tmp/generators') setup :prepare_destination # test "generator runs without errors" do # assert_nothing_raised do # run_generator ["arguments"] # end # end end
テストの雛形。rspec 使ってれば rspec で出してくれる。
...と思いきや、rspec の場合はスケルトン作成に失敗する。
(失敗の原因を詳しく追ってない。本当は作られるはず... なのかな)
継承クラス
親クラスは若干注意が必要なので、ここでやりたいことに合わせて分岐する
Rails::Generators::NamedBase
default だとこっち。
rails generate
コマンドの引数として、一つ何かを受け取ることが必須のクラス
cf. Rails::Generators::NamedBase
% rails generate controller Users xxxx
のような馴染みのある generator を作りたい場合はこちらで良い。
Rails::Generators::Base
NamedBase とは異なり rails generate
コマンドの引数を自由に定義できるクラス。
Rails::Generators::NamedBase
は Rails::Generators::Base
を継承している。
こちらのほうが柔軟性があるが、特別な用途がない限りは使わなくても良いかと思います。
便利メソッド
generator なので、テンプレートからファイル生成したり、ディレクトリを掘ったりする用途が主である。
rails はすごいから、そういう簡単なものは既にメソッドが用意してある。
これらのメソッドは全て Thor::Actions
に定義されているので、そっちを見たほうがいい。
というのも Rails::Generators::Base
が Thor::Actions
を include しているから。
もっと言うと Rails::Generators::Base
は Thor::Group
を継承して作られているので、rails の generator は大体 thor でできてると言って良い。
したがって困ったときは thor で検索すると良い
以下は便利メソッドの内、いくつかを紹介
create_file
directory
directory を copy する
template
ERB テンプレートを使って、ファイルを生成
具体例
class TestGenerator < Rails::Generators::NamedBase source_root File.expand_path('../templates', __FILE__) def create_empty_file create_file "aaaa.txt" end end
% rails g test arg1
create aaaa.txt
見たことある出力やー
% rails g test arg1
identical Test.txt
もう一回やると、またまた見たことある出力に。
大体こんな感じでノリで generator 作れてしまいます。すごい簡単です。
まとめ
- 継承するクラスによって、generator に渡せる引数が変わる
Rails::Generators::NamedBase
は 1 つの引数を受け取るRails::Generators::Base
は自在に定義可能
rails generator
の内部は殆どthor
- 詰まったら
thor
で調べると良い thor
を一度触ったことがある人は知見を十分に活かせるthor
はとても素直な実装になっているからthor
の spec はとてもわかり易いので、読むだけで理解できる
- 詰まったら
hook_for
は謎が多いので、使う時は若干注意thor
は「トール」と読むらしい
参考
CircleCI から deploy させる話
イントロ
CircleCI からテストが通ったら capistrano 使って deploy させたい。
けど、対象サーバーは IP 制限がかかっている。CircleCI の IP なんてコロコロ変わるし、どうしたらいいんや... って話。
これができると、PR を github ボタンでマージしたら、自動(当然テストも通した後)で deploy されるので、非常に嬉しい。
方針
before deploy
- awscli を使い security group の inbound に自分自身の ip を付与
- CircleCI の内部から IP address を割り出して、コマンドラインで一時的にinboud に追加させる
deploy
- コマンド(
bundle exec cap deploy
)を実行
after deploy
awscli
を使い security group から ip を取り外すbefore deploy
の逆をやればいい
前準備
IAM user や security group の名前は適当に決めてます。
やること多いです。
- IAM user
circle_ci
を作成- ec2 に関する権限を全て付与(今回に限って言えば、全て付与しなくても動くはず)
- 詳細は後述
- CircleCI 用の security group を作成する
- 名前は
circle_ci
- 普段は
inbound
の欄は空 - 一時的に CircleCI の IP が許可される用の security group
- 名前は
- deploy したいサーバーに security group
circle_ci
を attach する - AWS の環境変数を CircleCI の設定画面上で予め登録する
- IAM user
circle_ci
の credential を登録
- IAM user
- deploy 対象のサーバーにログインするための秘密鍵を CircleCI の設定画面上で登録する
実装
CircleCIからCapistranoを利用してAWS(EC2)にデプロイする - Qiita に大体書いてある。
deploy するコマンドとして、deploy.sh
みたいなシェルスクリプトを作って、それを deployment で実行させればいい。
参考記事とは少し違っているけど、自分が作成した deploy.sh は以下のようになった。
#!/bin/sh set -ex SECURITY_GROUP_NAME="circle_ci" IP=`curl -f -s ifconfig.me` trap "aws ec2 revoke-security-group-ingress --group-name ${SECURITY_GROUP_NAME} --protocol tcp --port 22 --cidr ${IP}/32" 0 1 2 3 15 aws ec2 authorize-security-group-ingress --group-name ${SECURITY_GROUP_NAME} --protocol tcp --port 22 --cidr ${IP}/32 bundle exec cap prod deploy
追記
コメントを受け、修正しました
Before
IP=`curl-s ifconfig.me`
After
IP=`curl -f -s ifconfig.me`
-f
は man で見てみると、以下のような意味だそうです。
確かエラーになった時の事を考えると、入れておいたほうが良いかもですね。
-f, --fail (HTTP) Fail silently (no output at all) on server errors. This is mostly done to better enable scripts etc to better deal with failed attempts. In normal cases when an HTTP server fails to deliver a document, it returns an HTML document stating so (which often also describes why and more). This flag will prevent curl from outputting that and return error 22. This method is not fail-safe and there are occasions where non-successful response codes will slip through, especially when authentication is involved (response codes 401 and 407).
ポイント
set -ex
-x
は毎度おなじみ debug 用途。実行したコマンドを標準出力するオプション。
-e
はコマンドに失敗したら、即時にスクリプトを終了する。一行づつ &&
で挟んでいるイメージ。
コマンドが 3 つ書かれているけど、どれか一つでも失敗したら、deploy せず即時失敗扱いで良いので、このようにしている。
trap コマンド
何かしら異常があった場合、変更を加えた Security Group を元に戻さないといけない。
でないと、ある IP から接続可能な状態になってしまい、セキュリティ上良くないから。
つまり、deploy に失敗しようが成功しようが、revoke-security-group-ingress
は実行させたい、ということ。
そのために trap コマンドでシグナルを補足しています。
0 1 2 3 15
を書いておけば、大抵のシグナルには反応してコマンドが発動する。
肝は最終的な exit コードは trap コマンドの実行結果ではなく、trap 以前に実行した最後のコマンドであること。
つまり bundle exec cap prod deploy
で exit コード 1 が返ってきた場合、trap が発動して、revoke
され、exit コード 1 でシェルスクリプトが終了する。
(CircleCI では exit コード 1 ならば、build 失敗扱いになる)
circle.yml
circle.yml は以下のようになる。
pip install しないと awscli
のバージョンが古く、コマンド実行に失敗する。
machine: timezone: Asia/Tokyo ruby: version: 2.1.3 dependencies: pre: - sudo pip install awscli deployment: master: branch: master commands: - ./deploy.sh
まとめ
ここまで準備して、ようやく master へのマージ + テスト通ったら deploy が自動化されます。
とはいえ、これでも revoke-security-group-ingress
に失敗する可能性があり、CircleCI の IP address が許可されたままになる可能性があります。が、そのリスクには目を瞑っています。
revoke-security-group-ingress
に失敗したら build にも失敗するはずなので、すぐに調査すればいい話だから。
circle_ci
の inbound を削除すれば元通りなので、復旧作業も楽勝。と思ってる。
なお、Circles EC2 IP addresses and AWS security group - CircleCI なんてのがあって、この情報をうまく使えば deploy できるんや... って思いがちだけど、これは孔明の罠なので要注意。
できてないこと
pip install awscli
をキャッシュさせること- 頑張れば出来そうだけど、まだやってない