scramble cadenza

技術ネタのガラクタ置き場

authlogic で blank の password で update 出来てしまう問題

イントロ

情報が古いけど

ruby on rails - changing password with authlogic - validation not catching blank inputs - Stack Overflow
と同じ現象。

改めてまとめると、以下の様な状態。

  1. authlogic でログインユーザーを作成
    • ログインユーザーは password を form に入力する
  2. ログインユーザーを編集する画面を作る
    • 新規作成ではないので注意
    • password を編集可能にする
  3. ログインユーザーを編集するとき、パスワードを空にして保存できてしまう

解決法

class User < ActiveRecord::Base
  acts_as_authentic do |c|
    c.ignore_password_blank = false
  end
end

とする。

よく見ると

https://github.com/binarylogic/authlogic/blob/23751e3eaba4c2b09751505079d7264fd1cc8b72/lib/authlogic/acts_as_authentic/password.rb#L44-L54

のコメントに書いてある。

前提条件も含めて説明すると...

  • 編集画面において、password を最初から入力済みにすることはできない
    • DB に保存されている password を復号することはできないため(暗号化方式によるが)
    • つまり編集画面を開いた直後はどうしても空になってしまう
  • 状況として「 password は変えたくないけど、ページを遷移してしまう」ケースが存在するだろう
    • どういうケースかは明記されてないけど、例えば間違って「更新」ボタンを押してしまったりするとか?
    • そうすると、空のパスワードが保存されてしまい、セキュリティ的に甘くなる
      • そういうケースは避けたい
      • オプションで変更できる

ということらしい。(多分)

Capybara::Poltergeist で remote debug が動かない件

イントロ

タイトルの通り。Capybara::Poltergest の目玉機能である remote debugging がうまく動作しなくて、色々試行錯誤した話。

https://github.com/teampoltergeist/poltergeist#remote-debugging-experimental

環境

  • OSX 10.10.3
  • phantomjs 2.2.0
  • poltergeist 1.6.0
  • chromium 45.0.2411.0

remote debugging とは

poltergeist の README に書いてあるとおり

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, js_errors: false, inspector: true)
end

@session = Capybara::Session.new(:poltergeist)

@session.visit url
@session.driver.debug

のような感じで、driver.debug のタイミングでブラウザを起動し、debug できる機能らしい。

使い方は簡単で

  • Capybara::Poltergeist::Driver.newinspector: true オプションを渡す
  • driver.debug を呼ぶ

だけで良い。

苦戦した所

1. そもそもブラウザ起動しない

# @session.driver.debug を使った spec を実行させる
bundle exec rspec 
...

Poltergeist execution paused. Press enter to continue.
The file //localhost:9664 does not exist.

てっきり chrome か何かが自動で起動するんだろうなぁと思ってたが、file //localhost:9664 がないと言われて怒られてしまう。

解決法

微妙なアプローチだけど、chromium を install して回避してみた。

brew install Caskroom/cask/chromium
ln -s /opt/homebrew-cask/Caskroom/chromium/latest/chrome-mac/Chromium.app/Contents/MacOS/Chromium /usr/local/bin/chromium

として、chromium コマンドをむりやり PATH に追加。
これだけで良い。

というのも、ブラウザ起動の仕組みは単純で、 chromium chromium-browser google-chrome のいずれかのコマンドが PATH に通ってれば実行される、というものだから。

https://github.com/teampoltergeist/poltergeist/blob/9b73d618688c5cbdeaea600a193a514c58a754e6/lib/capybara/poltergeist/inspector.rb#L3

chromium http://www.google.co.jp

と叩いて chromium が起動して google トップページが表示されるならOKなはず。

google-chrome コマンドがどうやら linux 環境でのコマンドというのと、macgoogle-chrome コマンドに相当するものを見つけられなかったので、こんな感じになりました。

Check failed: feature. と言われて起動できない。

ところが上記をやっても解決しない。
chromium が動こうとするのだが、以下の様なエラーが出て、chromium が起動できない。

Poltergeist execution paused. Press enter to continue.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:ERROR:component_loader.cc(154)] Failed to parse extension manifest.
[86324:1299:0524/032749:FATAL:feature_provider.cc(75)] Check failed: feature. FeatureProvider 'behavior' does not contain Feature 'whitelisted_for_incognito'

色々調べてみたがよくわからない。
chromiumソースコードにそれっぽいものを発見して、右往左往したけど挫折。

解決法

これは 予め chromium を起動した状態で、@session.driver.debug を実行する と回避できる。

要するに chromium 起動させて rspec 叩くわけです。

すると

f:id:mgi:20150524035806p:plain

のように file:///localhost:9664 を開こうとしているから、file:/// を消してエンター。

f:id:mgi:20150524035816p:plain
URL があらわれた。URL をクリック。

f:id:mgi:20150524035831p:plain

無事コンソール画面へ

まとめ

溢れんばかりのバッドノウハウ感。
poltergiest の experimental な feature なので、参考になれば。

google-chrome 使うことができれば、妙なエラーはでない?のかもしれない。

みんなどうやってるの...

bootstrap-generators で bootstrap 導入後、一部の flash が使えなくなる現象

イントロ

bootstrap 導入すると、flash がちゃんと動かなくなって戸惑うやつ。
decioferreira/bootstrap-generators で生成する flash 部分にミスがあるのが原因(多分)

具体的には flash[:alert] 時でも、緑枠の flash メッセージになってしまう現象。
ちゃんと考えると直せるんだけど、思い出すのが面倒なので残す。

環境

  • rails 4.2.1
  • sass-rails 5.0.3
  • bootstrap-sass 3.3.4
  • bootstrap-generators 3.3.1

各 gem の install 方法なども省略。 README.md の通りです。

bootstrap-generators について

意外とスター数が少ないので補足。

railstwitter-bootstrap 導入しようとすると、有名なのは以下の2つ。

bootstrap-sass のほうが簡単に導入できるが、generator が付いていないので、view 等が手作りになってしまう。
一方で twitter-bootstrap-rails は導入が面倒だが、generator が便利で、一回インストールできてしまえば楽になってくる、という感じ。

正に一長一短な2つの gem なわけですが、実は boostrap の generator は gem になって切りだされていて、bootstrap-sass と合わせることで「install 簡単」と「generator」のイイトコロ取りをできるわけです。

その generator が decioferreira/bootstrap-generators です。

rails generate bootstrap:install

これで bootstrap のあの黒いヘッダー(やそれ以外も)が生成されます。
haml/slim/erb に対応している。

直し方

app/views/layouts/application.html.erb (bootstrap 導入後)

の class を作っている部分を直す。

<% flash.each do |name, msg| %>
-  <%= content_tag :div, :class => "alert alert-#{ name == :error ? "danger" : "success" } alert-dismissable", :role => "alert" do
+  <%= content_tag :div, :class => "alert alert-#{ name == "alert" ? "danger" : "success" } alert-dismissable", :role => "alert" do
    <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
    <%= msg %>
  <% end %>
<% end %>

後はいつもどおり flash[:alert] してあげればよい。

if @user_session.save
  redirect_to root_url, notice: "Login successful"
else
  redirect_to login_url, alert: 'Login failed.'
end

何も考えず redirect_to login_url, error: 'Login failed.' みたいにやっても効かないので注意。
(view で :error とあるから引っかかった。)

参考

おまけ

記述量を最小限にして直すと、上記のようになるけど、いやちゃんと考えたい、という場合には以下が参考になる。
以下の 3 つのうち、どれかを使えばひとまずは大丈夫そう。
(要は flash のキーが bootstrap のクラスに対応していればいいだけなので)

heroku で Postgres 9.4 を使う

イントロ

タイトルの通り。jsonb 型のカラムを取り扱おうとしたら、激しく怒られた。

rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
...
PG::UndefinedObject: ERROR:  type "jsonb" does not exist
LINE 1: SELECT 'jsonb'::regtype::oid
...
: SELECT 'jsonb'::regtype::oid/app/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:155:in `async_exec'
...

原因

Postgres の default version が 9.3 だから。jsonb 型は 9.4 からサポートされているので、そんな型しらねーよと言われている。

https://devcenter.heroku.com/articles/heroku-postgresql#version-support-and-legacy-infrastructure

解決法

https://blog.heroku.com/archives/2015/1/8/postgres-9-4-on-heroku

9.4 に upgrade して、DATABASE_URL を切り替えるだけでよい。

% heroku addons:add heroku-postgresql --version=9.4 --app test
Adding heroku-postgresql on test... done, v14 (free)
Attached as HEROKU_POSTGRESQL_YELLOW_URL
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pgbackups:restore.
Use `heroku addons:docs heroku-postgresql` to view documentation.

config:unset

% heroku config:unset DATABASE_URL --app test
Unsetting DATABASE_URL and restarting test... done, v15

config:set

% heroku config --app test
=== test Config Vars
....
HEROKU_POSTGRESQL_ROSE_URL:   postgres://xxxxx
HEROKU_POSTGRESQL_YELLOW_URL: postgres://yyyyy
....

upgrade した時のログにも書いてあった通りで、HEROKU_POSTGRESQL_YELLOW_URL という環境変数が attach されているので、この value を DATABASE_URL に set する。

heroku config:set DATABASE_URL=postgres://yyyy

再起動

migration して再起動して反映させる。

heroku run rake db:migrate --app test
heroku run rake db:seed --app test
heroku restart --app test

rails で id を BIGINT 型 + primary key + AUTO_INCREMENT する

イントロ

rails のモデルは自動で id というカラムを作ってくれますが、これは常に INT 型。
BIGINT 型にしようとしたら、結構ハマったのでメモする。

使っている database はmysql です。
(postgresql だとこの罠は回避できるのだろうか?)

tl;dr

  • 普通に頑張ると...
    • db:migratedb:setup の実行結果が異なる
      • migration ファイルの内容が、schema.rb に(一部)反映されないため
    • db:migratedb:setup の実行結果が異なると、 db:rollback ができなくなるので、辛くなりそう。
      • db:migratedb:setup どちらかを使わない、という運用方式もある
      • だが、面倒くさいので、そういうことを意識したくない
  • 解決方法は 2 つ

普通にやってみる

Rails探訪 ~ create_table 編 ~ | マネーフォワード エンジニアブログ の素晴らしい参考記事によると、こんな感じ。

(ところで参考記事を見ると、色々地雷を踏み抜いてこの結論なので、この時点で既に普通でない気もする)

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users, id: false do |t|
      t.column :id, 'BIGINT PRIMARY KEY AUTO_INCREMENT'
      t.string :name

      t.timestamps null: false
    end
  end
end

rake db:migrate

% rake db:drop db:create db:migrate
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

無事成功。

rake db:setup

一見うまくいってそうだが、実はこれは問題がある。

db/scheme.rbBIGINT 型にしたよ、という内容が反映されていないために、db:setup をやると db:migrate の結果と異なったものになってしまう。

% rake db:setup
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

INT型 に戻ってしまった。
じゃあどうするの?と言うと、schema_format = :sql 方式か activerecord-mysql-awesome 方式の二通りがある。

schema_format = :sql

よく見かける解決法。 デフォルトでは schema_formatruby なので、これを sql にする。
:ruby では db/schema.rb を生成するが、:sql にすると db/structure.sql を生成してくれる。

config/application.rb 等に予め設定しておく。

config.active_record.schema_format = :sql

rake db:migrate

migration ファイルはここと同じものを使う。

mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

bigint 型になっている。ここまでは良い

rake db:setup

% rake db:setup
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

今度は migratesetup の結果が同じになりました。

activerecord-mysql-awesome

kamipo/activerecord-mysql-awesome

を使う。 この場合 schema_format:ruby のままで良い。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users, id: :bigint do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end

のような migration ファイルを作る

rake db:migrate

% rake db:drop db:create db:migrate
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

rake db:setup

% rake db:setup
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

migratesetup の結果が同じになった。

change_column 編

create_table ではなく、change_column でやったらどうなるか。 create_table の時点で INTBIGINT にしようと思うことが少ないので、実は change_column で対応できるかどうかが重要。

create_table の時と違って、今度は db:rollback が成功するかどうかもチェックしなければならない。

activerecord-mysql-awesome / schema_format = :sql

sql の確認コマンドは鬱陶しいので省略。

class ChangeIdColumnToUsers < ActiveRecord::Migration
  def change
    reversible do |dir|
      dir.up do
        change_column :users, :id, :bigint, auto_increment: true
      end

      dir.down do
        change_column :users, :id, :int, auto_increment: true
      end
    end
  end
end

この migration ファイルで

  • rake db:drop db:create db:migrate
  • rake db:setup
  • rake db:rollback

が成功する。ばっちり。
activerecord-mysql-awesome でも schema_format = :sql でも同じ結果になったので、change_column の時はこうすれば良さそう。

普通にやる

勿論これも activerecord-mysql-awesomeschema_format = :sql をせずに migration しても、 db:setup で id が INT型 になってしまう。

おまけ

https://github.com/rails/rails/pull/18220 がマージされているので、現時点での rails の master では対応済みのよう。
なので、 第三の選択「もうしばらく待つ」 も有りかも。

少なくとも Rails 4.2.1 では修正は含まれていなかったです。

ubuntu で install 済みのパッケージリストを表示する

イントロ

yum list installed 的なやつ。
apt-get list のようなコマンドがあるだろうと思っていたが無かった。

コマンド

dpkg --get-selections

参考

http://www.howtogeek.com/howto/linux/show-the-list-of-installed-packages-on-ubuntu-or-debian/

embulk-filter-eval というフィルタープラグイン書いた

イントロ

まぁ半分ネタも入ってますが、何かちょっとした事を行う程度なら役に立つはず。

mgi166/embulk-filter-eval

これは何が便利?

input として与えられたカラムに対して、Ruby のコードでちょっとした変換を加える事ができる embulk プラグインです。
embulkversion 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_columnsid しか指定していないので

$ embulk preview config.yml

+---------+
| id:long |
+---------+
|       2 |
|       3 |
|       4 |
|       5 |
+---------+

こんな感じになります。

まとめ

またゴミのような gem を作ってしまった...
次はもっといいものを考えてます。

rbenv の plugins

イントロ

今使ってる rbenvplugin たち。
rbenv はこういうエコシステムが便利だから使い続けている。

最近務めている会社では流行りのようなので、私もやってみる。

plugins

% l .rbenv/plugins/
rbenv-ctags
rbenv-default-gems
rbenv-gemset
rbenv-update
ruby-build

順に紹介

rbenv-ctags

tpope/rbenv-ctags

ruby のコアライブラリにタグファイル(ctags 用)を作ってくれるやつ。 ruby のコアライブラリを追うときに便利。だが、これは vim 用のタグしか吐いてくれない。(作者が vim 使いだから?)

vim 使える人は本家を使えばいいのだけど、私は emacs しか使えないので、emacs 用のタグを吐くように、fork したものを使ってる。

mgi166/rbenv-ctags

PR 出すと vim マッチョな作者に殺されそうなのでしていない。

rbenv-default-gems

sstephenson/rbenv-default-gems

rbenv build を使って ruby を install した時に、自動で gem を install してくれるやつ。 一回入れてしまうと便利。有名。

少し変わったことといえば、mgi166/dotfilesdefault-gems ファイルをバージョン管理していて

rake install default-gems

~/.rbenv/default-gems ディレクトリにシンボリックリンクを貼ってくれるようにしたが、このメソッド、全く流行る気がしない。

% cat ~/.rbenv/default-gems
bundler
pry
pry-byebug
pry-stack_explorer
ripper-tags

デフォルトの環境汚したくないけど、汚しまくっていて、羞恥心がなくなりつつある今日この頃。

rbenv-gemset

jf/rbenv-gemset

一言で言うと rbenvrvm gemset gem を管理するやつ。bundler はプロジェクト単位で管理するけど、これは自在に namespace を作ることができる。

昔は gem 作るたびに gemset 作ってた。Gemfile 作るのめんどいけど、何か作ろうかなーって時に使う。
最近は何かやる際には gem にするつもりがなくても bundle gembundle 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

rkh/rbenv-update

rbenv update コマンドで、全ての plugin を git pull してくれるやつ。 これのお陰で ruby の新しいバージョンが出るたびに、毎回

cd ~/.rbenv/plugins/ruby-build
git pull

しなくてよくなった。 コマンド覚えやすいから好き。

ruby-build

sstephenson/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/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::Config はアクセサを定義しているだけ

みたいな事ができ、馴染みのあるコードになってきた。

けどこれだけだと、config_acessor の代わりに attr_accessor でいいんじゃね?ってなる。

オプションを渡す

ところがどっこい config_accessor にはオプションを渡すことができる。
instance_readerinstance_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 でいいんじゃないか...? って思いました。
けど使い方次第でしょうねー