日々雑記

旅行の事ばっかり。神社成分多め。
Recent Tweets @

Rails 4.2から導入されたweb-consoleについて試してみたのでメモ。なお、試したバージョンは’2.0.0.beta3’。Rails 4.2.beta1だと、2.0.0.beta2がGemfileに記載されているが、2.0.0.beta2はルーティングエラー時にエラーになるバグがあるのでbeta3で。

web-consoleとは

Rails 4.2のMajor Featuresの一つ。

デフォルトエラーページ用のデバッギングツールで、ブラウザ上からインタラクティブにconsoleの操作が出来る。

github上の画像引用


エラーが起きた箇所で操作が出来るだけではなく、traceを選択する事で、選択した箇所での値の確認も出来る。Better Errorsみたいなもん。

consoleは、エラーページだけでなく、任意のviewで表示する事が出来る。表示させるには、表示させたいviewでconsoleメソッドを呼び出すだけでOK。呼び出せるのは、development/test環境のみ。


consoleメソッドで呼び出す以外に、consoleを特定のURLにマウントさせる事も出来る。マウントさせるには、config.web_console.automount = trueを設定してあげればOK。因みに、この設定は将来はデフォルトtrueにするらしい。


デフォルトのマウント先は/console。こちらのパスは config.web_console.default_mount_path で変更可能。

他にも、接続元IPを制限したら、timeoutの設定等を行う事が出来る。詳細はconfiguration参照。

style

consoleは、スタイルの設定も行う事が出来る。設定出来るのは、colorとfont。

カラーテーマには以下のようなものがある。

  • monokai: the default Sublime Text colors
  • solarized_dark: light version of the common solarized colors
  • solarized_light: dark version of the common solarized colors
  • tango: theme based on the tango colors
  • xterm: the standard xterm theme

monokaiを設定するとこんな感じ。


カラーテーマは自分で定義する事も出来る。

  WebConsole::Colors.register_theme(:custom) do |c|
    # The most common color themes are the 16 colors one. They are built from 3
    # parts.

    # 8 darker colors.
    c.add '#000000'
    c.add '#cd0000'
    c.add '#00cd00'
    c.add '#cdcd00'
    c.add '#0000ee'
    c.add '#cd00cd'
    c.add '#00cdcd'
    c.add '#e5e5e5'

    # 8 lighter colors.
    c.add '#7f7f7f'
    c.add '#ff0000'
    c.add '#00ff00'
    c.add '#ffff00'
    c.add '#5c5cff'
    c.add '#ff00ff'
    c.add '#00ffff'
    c.add '#ffffff'

    # Background and foreground colors.
    c.background '#ffffff'
    c.foreground '#000000'
  end

  # Now you have to tell Web Console to actually use it.
  config.web_console.style.colors = :custom

fontはconfig.web_console.style.fontに値を設定する事で変更可能。デフォルトは、large DejaVu Sans Mono, Liberation Mono, monospace

まとめ

better_errorsを使っているので、無理に使わなくて良いかなーと思っていたのだが、マウントさせて使うのが割と便利だった。

ローカルの開発環境以外の環境(デザイナーさんや、コーダーさんの環境等)のデータをちょっと操作したい、という場合には重宝しそうなので、そういうケースが多い人には、割とおすすめかと。

大分久し振りの一宮巡り。東北編 Part3。 春に行った新潟の記事を書くのを忘れていた…。後でまとめて書くか。

鳥海山大物忌神社 吹浦口ノ宮

出羽国一宮、鳥海山大物忌神社。 まずは、吹浦口。

鳥海山大物忌神社は、名前の通りご神体は鳥海山にあり、本社は鳥海山山頂にある。ふもとには「口ノ宮」と呼ばれる里宮が吹浦(ふくら)と蕨岡(わらびおか)の二箇所に。

吹浦も蕨岡も鳥海山の登山口で、昔は他にも登山口があり、どこが一宮を名乗るかは結構揉めたらしい。詳細はwikiご参考

色々とあった結果、明治4年に、山頂を一宮、吹浦と蕨岡口ノ宮とする事で落ちついたとの事。一応三社併せた総称として「鳥海山大物忌神社」となっている。その後、改めて吹浦を口之宮と定めて社務所を置き、蕨岡は摂社として遇されて今に至っているとの事。

なので、宮司さんは吹浦に常駐しているので、御朱印が欲しいならとりあえず吹浦に来るのが良いかと。蕨岡にもいらっしゃる事あるらしいのだが、今回は会えなかった。

創祀は564年で、祭神は大物忌神(おおものいみのかみ)。由緒によると、倉稲魂命・豊受姫神と同神との事。記紀に存在しない神様なので、後付けで同神になったのかなあ。鳥海山噴火が兵乱の前兆であると信じられていた節があり、守り神的な位置づけでもあったらしい。

で、社内。

吹浦は月山命が合わせて祀られており、本殿が2つ。これについて詳細な説明が見つからなかったんだが、単純に月山が近いからかなあ。

決して大きい神社ではないのだが、拝殿も本殿も趣が感じられて非常に良かった。あと駅近。助かる。

祭神:大物忌神(おおものいみのかみ)、倉稲魂命・豊受姫神と同神。月山命(月読命)
アクセス:JR東日本羽越本線 吹浦駅 徒歩約5分

続いて蕨岡口へ。

鳥海山大物忌神社 蕨岡口ノ宮

吹浦口のある吹浦駅の隣、遊佐駅が最寄りの蕨岡口へ。

何はともあれ本殿がでかい。


これだと伝わりづらいですね…。ええ…。

説明によると、”桁行総長は13.8m、梁間の実長も16.9mに及び、床高も2.3mあまり〜”との事。国登録有形文化財にも登録されているとの事。

栄えていた事を伺わせる。神仏習合自体は、社僧もたくさんいらっしゃったんでしょうねえ。

他にも記念碑等見所の多い蕨岡口なのだが、アクセスは少々悪い。一番近い駅から大体5kmあるので、歩くとちょっと大変。大変だった。 バスも無いので、タクシー or レンタサイクルがおすすめ。

祭神:大物忌神(おおものいみのかみ) アクセス:JR東日本羽越本線 遊佐駅 大体駅から5km位。バス等は無いので、タクシー、又はレンタサイクル(駅でレンタルしている)がお勧め。

これで東北も完了。次は富山の予定。

折角Rails 4.2.0 beta1が出たので、個人で使ってるアプリをアップデートしてみました。

bundle update rails実行後rails sを実行すると、Sprockets::Rails::Helper::AssetFilteredErrorでエラーが。

エラー詳細は以下の通り。

"Asset filtered out and will not be served: add Rails.application.config.assets.precompile += %w( bootstrap-theme-white-plum/dist/fonts/glyphicons-halflings-regular.eot ) to config/initializers/assets.rb and restart your server”

sprockets-railsが2.1から、全ての環境でprecompile list必要になった事に伴い、 こちらのコミットで、assets.precompileについてはinitializersに定義するよう修正されたので、エラーメッセージの通りにconfig/initializers/assets.rbにassets.precompileの設定を追加した所、無事に起動しました。

因みに、developmentでもconfig.assets.digestsデフォルトがtrueになるよう修正されているので、ちゃんとprecompileの設定及びassetファイルを使用する場合は、ヘルパーメソッドを利用するよう対応した方が良さ気ですねえ。

この後ちまちま触っていたら何故かredirect_toメソッドがwrong number of arguments (2 for 1)でエラーになる現象が。

こちらは、turbolinksのバージョンアップを行っていなかったのが問題でした。上記エラーが出た場合、turbolinksを2.2.3に上げたらOKでした。

とりあえずはこんな感じで一通り動くようになりました。

次はMajor Featuresを試してみよう。

仕事でやる必要があったのでメモ。

やりたいこと

ログファイルに特定の文字列(例えば、”Error”など)があった場合に、メールで通知を行いたい。

インストール

今回の環境はUbuntuだったので、aptでインストール。

sudo aptitude install monit

設定ファイルの編集

aptでインストールした場合、デフォルトの設定ファイルは/etc/monit/monitrc。これは/etc/init.d/monitで指定されているので、 もしファイル自体はを変更したい場合は、monitファイルのCONFIGを変更してあげればOK。

設定項目については、monitrcについてちゃんとした説明があるので、詳細はそちら参照。参考までに、私が設定した内容は以下の通り。

set daemon 60
set logfile /var/log/monit.log
set idfile /var/lib/monit/id
set statefile /var/lib/monit/state
set mailserver localhost
set eventqueue
    basedir /var/lib/monit/events
    slots 100
set mail-format {
  from: monit@$HOST
  subject: monit alert --  $EVENT $SERVICE
  message: $EVENT Service $SERVICE
                Date:        $DATE
                Action:      $ACTION
                Host:        $HOST
                Description: $DESCRIPTION

           Your faithful employee,
           Monit
}
set alert xxxx@gmail.com  # receive all alerts
include /etc/monit/conf.d/*

ざっくり書くと、

  • daemon: デーモンとして動作させる場合の監視間隔を秒で指
  • mailserver: アラート送信先メールサーバ
  • mail-format: 送信するメールのフォーマット
  • alert: メールの送信先

という感じで。

ログファイルの監視設定

/etc/monit/monitrc自体に直接書けるのだが、一個のファイルに色々と書くと煩雑にで、今回は別ファイルで管理。 /etc/monit/monitrcの末尾にある include /etc/monit/conf.d/* が別ファイルの読み込み処理で、この書き方の場合、 /etc/monit/conf.d/配下のファイルが全て読み込まれる。

なので、そこに適当な名前でファイルを作成し格納する。中身は以下のような感じ。

check file tmplog with path /home/yaginuma/tmp/test.log
    if match "Error" then alert

割と見たまんまな気がする。

1行目は、

CHECK FILE <unique name> PATH <path>

というフォーマットなので、上記サンプルの場合、が”tmplog”、が”/home/yaginuma/tmp/test.log”になっている。

これで”/home/yaginuma/tmp/test.log”に”Error”という文字列が出力された場合、エラーメールが送信される。設定変更後にmonitの再起動を忘れずに。

上記の例だとメールを送信するようにしているが、任意のコマンド実行したりも出来る。詳細はdoc参照。monit便利。

戸田市花火大会

花火

シャッター速度:2.0、f/7.1、ISO:80だとよさげ。

少し調べたのでメモ。

constraintsとは

railsのroutingには”constraints”という機能があり、routingに様々な制限を設定する事が出来る。

設定出来るconstraintsについては以下。

HTTP Verb Constraints

HTTPメソッドでの制限。

match 'users', to: 'users#index', via: [:get, :post]

上記の場合、GETPOSTメソッド以外でusersアクセスしようとするとActionController::RoutingErrorがraiseされる。rails3系から4系に移行する際によく見た書き方ですな。

Segment Constraints

dynamic segmentのフォーマットについて制限を設定する事が出来る。

  get 'users/:id', to: 'users#show', constraints: { id: /[A-Z]\d{5}/ }

上記の場合、”/users/A12345”は通るが、”/users/893”はエラーになる。値を厳密に制限したいときは使える。

なお、正規表現にアンカーは使用出来ない。アンカーを使用すると、ArgumentErrorが起きる

get 'users/:id', to: 'users#show', constraints: { id: /\A[A-Z]\d{5}\z/ }
# => Regexp anchor characters are not allowed in routing requirements: /\A[A-Z]\d{5}\z/ (ArgumentError)

これは、routesが最初にアンカーを設定しており、constraintsでアンカーを設定する必要が無い為、エラーにしているもよう。逆に言うと、部分一致的な事は設定出来ない。(上記の例だと、”/usesr/A123456”はエラーになる。)

Request-Based Constraints

Requestの情報を元にした制限も出来る。Request objectがStringを返すメソッドをconstraintsに設定出来る。

get :users, to: 'users#index', constraints: { port: '3000' }

上記の場合だと、port番号が3000以外の場合エラーになる。他にも、subdomainの値やprotocolでの制限が可能。request objectについて、詳しくはこちら

Advanced Constraints

もっと複雑な事をしたい場合、constraintsに独自のクラスを指定する事も可能。指定するクラスには、matches?メソッドが定義されていればOK。

例えは、特定のIPからのみリクエストを許容したい場合、以下のように設定可能。

class WhitelistConstraint
  IP_LIST = %w(192.168.1.1 127.0.0.1)

  def initialize
  end

  def matches?(request)
    IP_LIST.include?(request.remote_ip)
  end
end


Rails.application.routes.draw do
  get 'admin/index', constraints: WhitelistConstraint.new
end

上記の場合、IPが192.168.1.1、127.0.0.1以外の場合エラーになる。

なお、上記の例だとインスタンスメソッドにしてるが、クラスメソッドでもOK。

class WhitelistConstraint
  IP_LIST = %w(192.168.1.1 127.0.0.1)

  def self.matches?(request)
    IP_LIST.include?(request.remote_ip)
  end
end


Rails.application.routes.draw do
  get 'admin/index', constraints: WhitelistConstraint
end

特に初期化処理が無い場合は、こっちの方がスッキリする気がする。

lambdaでも出来る。

get 'admin/index',  constraints: ->(request) { WhitelistConstraint::IP_LIST.include?(request.remote_ip) }

routingが汚くなってしまうので、余程短くない限りはクラスを作成してmatches?メソッドを定義した方が良いかなあ。

同じconstraintsを複数のroutingに設定する場合、ブロック設定出来る。

  constraints(WhitelistConstraint) do
    get 'admin/index'
    get 'admin/list'
  end

まとめ

あまり使われて無いような気がするんですが、例えば管理画面にアクセスするIPを制限したい、という場合には割と便利かと。
HTTPサーバで設定出来ればその方が良いと思うのですが、例えばHerokuのようなHTTPサーバを触れないPaaSとかでは、controllerにアクセス制限書くより、routingにあった方がスッキリする時もあるあと。

因みに、先に挙げた例だと、WhitelistConstraintクラスをroutes.rbに直書きしているのですが、実際はそんな事をせず、当然別クラスにするのですが、そのファイルをどこに格納するかで微妙に迷っている。

結局、libの下にconstraintsディレクトリを作成しそこに格納しているのですが、もっと良い場所がある、という方がいたら、教えて頂けますと助かります。

minitest-soundというgemを作ったのでご紹介。

内容

名前の通り、minitestのpluginです。ソースはこちら

minitest実行中、及び、テスト成功/失敗それぞれのタイミングでmp3を再生する事が出来ます。それだけ!

mp3の再生には、mpg123というライブラリを使用しています。コマンドラインでmp3再生出来るので便利。

使い方

先に記載した通り、mpg123を使用しているので、mpg123をインストールしてない場合、まずmpg123のインストールをして下さい。 Ubuntuの場合sudo aptitude install mpg123でOK。他のOSも、(多分)パッケージ管理システムからインストール出来る筈。

次に、お決まりの通り、Gemfileにgem 'minitest-sound'の追加及びbundle installを実行して下さい。

最後に、test_helper.rbに以下の内容を設定して下さい。

require 'minitest/sound'

Minitest::Sound.success = '/aaa/bbb/xxx.mp3' # テスト成功時に再生するmp3ファイル
Minitest::Sound.failure = '/aaa/bbb/xxx.mp3' # テスト失敗時に再生するmp3ファイル
Minitest::Sound.during_test = '/aaa/bbb/xxx.mp3' # テスト実行中に再生するmp3ファイル

ファイルのパスは全てフルパスで。以上で設定完了。

後は普通にテストを実行するとmp3が再生されます。再生されない場合、ファイルのパスが間違っている可能性があります。

今後やりたい事

  • テストが100回成功したらマリオの1upのSEを流す、というネタをtwitterで見かけたので同じ事をやりたい
  • mp3ファイルをローカルに置くと、環境が変わった時に困るので、SoundCloud辺りのクラウドサービスから音源を再生出来るようにしたい。ただ、そうすると、今度はアップロードの手間が。うーん。

色々と試してみたのでメモ。なお、下記はrails 4.1.1で検証しています。幾つかのパターンでエラーになる事がありますが、最新のバージョンでは直ってる可能性もあります。

Action Mailer Previewsとは

Rails4.1から入った機能で、名前のとおりemailのpreviewが出来る機能。

同じ機能を持つものとして、mail_viewというgemがあり、Mailer Previewsはこのgemを参考に作られている。

使い方

早速試してみる。まずは、generatorでmailerを作成。

rails g mailer UserMailer mail1
      create  app/mailers/user_mailer.rb
      invoke  erb
      create    app/views/user_mailer
      create    app/views/user_mailer/mail1.text.erb
      create    app/views/user_mailer/mail1.html.erb
      invoke  test_unit
      create    test/mailers/user_mailer_test.rb
      create    test/mailers/previews/user_mailer_preview.rb

mailerを作成すると、test/mailers/previews/xxxx.rbというファイルが作成される。これがpreviewする為のクラスになる。中身は以下の通り。

# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

  # Preview this email at http://localhost:3000/rails/mailers/user_mailer/mail1
  def mail1
    UserMailer.mail1
  end
end

先ほと作成したmailerのメソッドを呼び出しているだけ。

後は、コメントに書いてある通り、http://localhost:3000/rails/mailers/user_mailer/mail1 にアクセスすると、メールの確認が出来る。お手軽。

画面では、From、To、Date、Subject、bodyを確認出来る。また、body部分については、HTML/plain-textを選択する事が出来る。

ActionMailer::Preview クラスにメソッドを追加するとURLが生成されるので、同じメールで引数を変えて試したい、という場合はメソッドを増やしてあげればOK。

# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
  def mail1
    UserMailer.mail1('hi')
  end

  def mail2
    UserMailer.mail1('Yeah!!!')
  end
end

これでhttp://localhost:3000/rails/mailers/user_mailer/mail2にアクセスすると、追加したメソッドの方でメールの確認が出来る。

http://localhost:3000/rails/mailersにアクセスすると、PreviewsのURLが全て確認出来る。

named route もあるので、一覧ページへのリンクを作成したい場合、rails_mailers_url (又はpath)でOK。

mailer previewsクラスの格納先について

先に書いた通り、デフォルトだと test/mailers/previews配下に格納されるが、これは変更可能。

config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"

こんな感じで指定してあげればOK。 確認用URLは変わらず http://localhost:3000/rails/mailers/配下。

因みにデフォルトがtest配下なのは、app配下だとproduction環境でeager loadの対象になってしまうから、と何処かで見た気が…。出典があいまい。

register_preview_interceptor について

ActionMailerではメール送信前に処理を行うインターセプタを登録する事が出来て、MailerPreviewsでも同じように設定が出来る。

API docを見ると、configにregister_preview_interceptorを設定してあげればOKと書いてあるのだが、どうにも動かない…。 ソース見てみたが、configの値を参照してないような…。

ActionMailer::Base.register_preview_interceptorを直接呼び出してあげれば動いた。

# config/initializers/action_mailer.rb
class InterceptorSample
  def self.previewing_email(message)
    # something to do
  end
end

ActionMailer::Base.register_preview_interceptor(InterceptorSample)

docが間違えている気がする。後でもうちょい調べる。

attachments /attachments.inline を使用している場合

この辺りの動きが大分怪しい。

rails4.1.1では、attachmentsを使用している、かつ、HTML/plain-text両方のメールがある場合に、Mailer Previewsがエラーになって動かない。因みに、メールがHTMLだけ、又はplain-textだけの場合は、正常に動作する。

attachments.inlineを使用している場合は、とりあえずエラーになる。

詳細はこのPR参照。対応自体は完了しているように見えるが、Milestoneが4.1.3になっているのが気になる…。

その他注意事項

production環境では動かない、というかdevelopment環境でしか動かない。Rails.env.development? がtrueの時のみpreviewのroutingが追加されるようソース内で設定されている。

特に問題無い気もするが、development以外の環境で確認したい、というケースがあった場合に困る……かなあ。

まとめ

簡単にメールが見れるのはとても便利なのだが、attachments周りで不具合があるのがちと残念。4.1.3位には問題無く使えるのかなーと。

サンプルがこちらにあるので、もし興味ありましたらどうぞー。

宣伝

そんなAction Mailer Previewのソースのコードリーディングを、次回のginzarbで実施予定です。6/17実施なので、宜しければどうぞー。

意外と日本語の情報無かったのでメモ。みんなrspec使ってるからかしら。

幾つかやり方があると思うが、とりあえず公式のREADMEを参考に。

ファイル名

minitest/XXX_plugin.rbという名前にする。

minitest本体でGem.find_files("minitest/*_plugin.rb")を行っており、上記命名規則に則ると自動でrequireしてくれる。

初期化処理

plugin_XXX_initという名前のメソッドを定義すると、テスト起動時に自動で呼び出される。XXXの部分は、ファイル名と一緒である必要がある。違うと呼ばれないので注意が必要。

オプションの設定

plugin_XXX_optionsという名前のメソッドを定義すると、こちらもテスト起動時に自動で呼び出される。XXXについては、初期化処理同様ファイル名と合わせる必要あり。

ここまでをまとめるとこんな感じ(先で説明する部分も含まれてます)。

# minitest/sample_plugin.rb:

require_relative 'sample_reporter'
require_relative 'sample_reporter2'
module Minitest
  def self.plugin_sample_options(opts, options)
    opts.on "--sample", "Sample Plugin" do
      options[:sample] = true
    end
  end

  def self.plugin_sample_init(options)
    self.reporter << SampleReporter.new(options)
    self.reporter << SampleReporter2.new(options)
  end
end

上記の例だと、sampleの部分を合わせる必要がある。

plugin_XXX_optionsの引数が上記例だと少し解りづらい気もするが、第一引数のoptsOptionParserで、第二引数のoptionsがminitest内部で持っているパラメータ(実体はHash)。何か値を設定したい場合は、第二引数のoptionsに値を設定する。

なお、実行順序はplugin_XXX_optionsの方が当然先。

reporter

minitestではテスト結果を表示するのに、複数のrepoterクラスのインスタンスを使用している。

なので、テスト実行時に何か処理を行いたい場合、独自のreporterクラスを定義して、self.reporterに追加すればOK。上記例のplugin_sample_initメソッドでやっている、self.reporter << SampleReporter.new(options)の部分がそれ。

reporterで呼び出されるメソッドについては、AbstractReporterクラスで定義されている。なので、AbstractReporterを継承したクラスを作成し、メソッドをオーバーライドしてあげれば良い。

定義されているメソッドは以下の通り。

  • start  : テスト起動時に呼ばれる
  • record  : 各テスト終了時に呼ばれる
  • report  : 全テスト終了時に呼ばれる
  • passed? : 全テスト終了時に呼ばれる.戻り値にfalseを設定すると、testはfailになる.

recordは引数に実行したテストのインスタンスが渡される。テストが成功/失敗等の情報はそこから取得可能。詳しくはMinitest::Testクラス辺り参照。

passed?は全テスト終了時に呼ばれて、戻り値がtrueならテスト成功、falseならテスト失敗と判定される。テストは全部通ったけど、何らかの理由で失敗とみなしたい場合に使う。

以下サンプル。

module Minitest
  class SampleReporter < AbstractReporter
    attr_accessor :results

    def initialize(options)
      self.results = []
    end

    def start
      $stdout.print "test start!!\n"
    end

    def record(result)
      # print test name and execute time
      $stdout.print "%s#%s: %.3f s\n" % [result.class, result.name, result.time]
      self.results << result
    end

    def report
      $stdout.print "test finished!!\n"
    end

    def passed?
      true
    end
  end
end

これでテスト結果見て色々処理出来るようになったが、AbstractReporterクラスは何も行わないので、単純に”テストが何件失敗したか”等の情報が欲しい場合、自分で計算しなくてはならないので、ちょっと面倒。

そんな時は、StatisticsReporterを使用すると良い。

StatisticsReporterではassertions、count、results、start_time、total_time、failures、errors、skipsの値を保持しているので、そちらを参照するだけで済む。

module Minitest
  class SampleReporter2 < StatisticsReporter
    def report
      super
      $stdout.print "test time: #{total_time}\n"
    end
  end
end

基本的には、StatisticsReporterを継承してreporterを作るのが良いと思われ。StatisticsReporterについて、詳しくはソース参照

こんな感じで。