rubyのfixture replacementについての文句

何年ぶりなんだろ。おひさしぶりです。

7年ぶりの投稿は超初心者ネタ。 rubyのfixture replacementってみなさん何使ってますか? まぁ factory_bot ですよね。わたしもそうです。

factory_botって真面目にassociationを表現しようとするとなんか直感的に感じない。。。

class Product < ApplicationRecord
  has_many :items
end

class Item < ApplicationRecord
  belongs_to :product
end

というような関連のモデルがあるとき、

FactoryBot.define do
  factory :poduct, class: 'Product' do
    items [:low_item, :high_item]         # こんな感じに使いたい!
  end
  
  factory :low_item, class: 'Item' do
  end
  factory :high_item, class: 'Item' do
  end
end

としたいんですよ。

でも、factory_botのドキュメント では

FactoryBot.define do
  factory :poduct, class: 'Product'
  
  factory :low_item, class: 'Item' do
    accosiation :product, factory: :product
  end
  factory :high_item, class: 'Item' do
    accosiation :product, factory: :product
  end
end

このようにやりたいことの逆の定義になるっぽい。

ruby tool box で他のライブラリを探してみたんですが、factory_bot一強なんですね。うむー。

herokuにデフロイした時のメモ

2013年7月14日 タイムゾーンに関する記述を追加しました。

概要

無職中で暇なので、Herokuを使ってみました。Herokuはアカウントだけ作って触ったことなかったので、ドキドキでつまずきまくったけど、実質1時間くらいでデフロイできました。たぶん、普通にrails開発をしていて以下のことをすればHerokuへのデフロイは完了すると思います。

参考URL

他の参照したページもあるのですが、内容が重複していたり、情報として古くなったものもあったので、主観で覚えときたいリンクだけを残しました。

Herokuにデフロイする時に特別にした作業

heroku-toolbeltをインストールしてHerokuにログイン

昔は heroku っていうgemを使ってたような話を聞いたことがあるけど、今はコマンドラインツールをHerokuが提供してくれているらしい。

   brew install heroku-toolbelt

でインストール完了。

次に

    heroku login

ってコマンドを打つと、

  Enter your Heroku credentials.
    Email: hogehoge@email.com                 # Herokuでログインしているメールアドレス
    Password (typing will be hidden):         # Herokuのパスワード
    Found the following SSH public keys:
    # ~/.ssh/にあるファイルを自動的に探してくれる。複数ある時はどっちを使うか聞かれる。
    Which would you like to use with your Heroku account? 2
    Uploading SSH public key /Users/hoge/.ssh/id_rsa_hogehoge.pub... done
    Authentication successful.

こんな感じで

  1. ログイン
  2. sshの登録

をしてくれる。

環境変数の扱い

Herokuコマンドで環境変数を設定することをよく書かれているけど、僕はfigaroっていうgemを使っていたので、

   rake figaro:heroku

ってすると開発環境で使用している環境変数をHerokuに設定してくれます。

sqlite3をdevelopment、test グループに移動して pg をproductionにした

これは有名なことなので詳細は割愛。

rubyのバージョンを指定した

本日(2013年7月1日)現在、Heroku上のデフォルトのrubyバージョンは2.0です。

ruby1.9.3を開発時使用していたら、Gemfileに

   ruby "1.9.3"

と書きましょう。

rails_12factor gem を入れた

HerokuのGetting Started with Rails 4.x on Heroku と公式に書いてあるのでそのまんま入れといた。

上記リンクには「静的なasset機能とlogの機能をうまく処理してくれる」っぽいことが書かれていた。

config.assets.initialize_on_precompile = false を追加した

知っておきたい!Herokuを使う上では当たり前?の16の常識 にとにかく「書いとけ」とのこと。

タイムゾーンを設定した

アプリケーション内のタイムゾーンを設定します。 しかし、これをしても heroku logs で見れる時刻は設定したタイムゾーンを示してくれません。

    heroku config:add TZ=Asia/Tokyo

他の記事で書かれていなくて、心配だったこと

開発時はsqlite3を使っていたので、database.ymlは全く変更していませんでした。当然production環境の設定もsqlite3です。 dbの接続情報もHeroku上では公開されてなさそうだしどうしたものか。。。

しかし、この心配は杞憂に終わりました。

普通にHerokuにpushすると

    -----> Writing config/database.yml to read from DATABASE_URL

とログに出ていました。Herokuが勝手にやってくれるらしい。

あやふやなこと

Herokuを使う時、アセットパイプライン関係でハマるというイメージをなんとなく持っていたんだけど、とくにはまらなかった。

対策っぽいことをしていたことはしたけど、「アセットをプレコンパイルしたよー」って以下のログが出たのは

  -----> Preparing app for Rails asset pipeline
           Running: rake assets:precompile
           Asset precompilation completed (49.11s)

sass-railsをassetグループから外した後にデフロイした後だった。

rails_12factorを入れたり、 config.assets.initialize_on_precompilefalse にしたりってのは不要なのかな。。。

HerokuではじめるRailsプログラミング入門

HerokuではじめるRailsプログラミング入門

el-getのinfoディレクトリ先を確認する

el-get導入に伴って、infoディレクトリ先を指定したい。デフォルトはどこなのか?カスタマイズ出来る変数はあるのかを確認したい。

読んだソースは4fe798dbのコミット分。

el-get-installの定義から順を追ってみたけど挫折したので、ソースからinfoっぽい文字列を探す。

    M-x moccur-grep-find
    \binfo\b .*el$

で検索すると、早速それっぽいの発見。

el-get-install-or-init-info

el-get-build.el内に定義されている。

ここをななめ読みするとどこかの .info っていうディレクトリに入れられるっぽい。斜め読みしていると後から困りそうなのでもう少し読む。 .info に入るって思った根拠はここ。

    (list (list el-get-install-info
                (if (string= (substring infofile -5) ".info")
                    infofile
                  (concat infofile ".info"))
                "dir"))

infofile を設定している元を追う。

  • infofile は let* 内。
    (infofile (if (and (file-exists-p infodir-abs-conf)
                       (not (file-directory-p infodir-abs-conf)))
                  infodir-abs-conf
                (concat infodir-abs pname))))

いろいろしているけど、 infodir-abs-conf を元に設定される。

  • infodir-abs-conf は pdir と infodir を結合したもの。
    (let* ((infodir-abs-conf (concat pdir infodir))
  • pdir は el-get-package-directory から取得している。
    (pdir     (el-get-package-directory package)))

el-get-package-directory

el-get-core.el内に定義されている。

パッケージ名と el-get-dir を結合したものを返す。

el-get-dir

el-get.el内に定義されている。

デフォルトは ~/.emacs.d/el-get/ 実際にel-getがパッケージをインストールする場所。

結論

el-getでインストールされるinfoは各elispがインストールされるパッケージディレクトリ配下の .info ディレクトリ配下に出来上がる。変数によるカスタマイズは出来ない。 でも、el-getでパッケージのバージョンごとのinfoの管理まで出来ることになるので、あまり問題はないでしょう。

Emacs Lispのシンタックスハイライト出来ないんですね。。。

era_ja0.2.0公開

西暦から和暦に変換するgem、era_jaをアップデートしました。

アップデートの内容

漢数字を出力出来るようにしました

今回のアップデートではフォーマット文字列に漢数字を追加しました。

こんな感じで出力出来ます。

Date.new(2013,2,11).to_era("%O%JY年%Jm月%Jd日")
=> 平成二十五年二月十一日

年、月、日を表すフォーマット文字列の前に %J をつけると漢数字として認識します。年、月、日なので、当然西暦も漢数字に変換出来ます。

Time.mktime(2013,2,11).to_era("%JY.%Jm.%Jd")
=> 二千十三年二月十一日

バグフィックス

EraJa#to_era のフォーマット文字列で %Y %y などの西暦をそのまま出力する時、うまく変換出来ていない場合に対処しました。

Date.new(2013,2,11).to_era("%Y年%Jm月%Jd日")
=> 13年二月十一日

となっていたものを

Date.new(2013,2,11).to_era("%Y年%Jm月%Jd日")
=> 2013年二月十一日

と出力出来るようにしました。(あまりユースケースが思いつかないけど、、)

西暦から和暦に変換するコードを毎回ごにょごにょ書いている方はぜひお試しを!!!!

era_jaリポジトリ

反省点など

スピードが遅い

開発のスピードが落ちちゃってますね。。。まぁやることは和暦への変換だけという簡単すぎるライブラリなので、改善するネタもあまりないんだけど。。。でも、クローンしてくれた方が出てきてくれててモチベーション上がりました。ありがとうございます!

rake releaseっていいよ

今までなんとなくbundlerの rake release を使っていなかったけど今回のリリースで初めて使ってみました。なんで毛嫌いしてたんだろ?っていうくらい簡単にGitHubへのpushとrubygemsへのリリースが出来てしまった。食わず嫌いはイカン。

でも rake release ってやるとtagメッセージがただのバージョン名になっちゃうんですよね。今までそのバージョンでの大まかな変更点を書いていたんだけど統一感がなくなってしまった。まぁtagメッセージってあまり見ないからいいかということで。

shared_exampleいいよ

このライブラリは僕にしてはrspecshared_example を多用していて、コードをいじる時に shared_example のお陰でかなり見通しがよくなっていました。慣れてないので始め見た時は「なんだ?」って思ったんだけど概要が理解出来るとかなり捗る。

shared_exampleの置き場

リリース後の今になって思うけど、 shared_example の置き場って spec_helper.rb でいいのかなぁ。他のコードを見てみよう。

Capybaraのtableishもどきを読む

こちらのgist

page.find('table').all('tr').map { |row| row.all('th, td').map { |cell| cell.text.strip } }

というふうに以前のcucumberでできていたtableishをcapybaraで実現するコードが紹介されています。railsなどのwebアプリのテストで、一覧の出力順を含めたテストなどで非常に役立ちます。ただ、短いコードながら毎度毎度、読んで忘れて読んで忘れてを繰り返しているので、ちょっとした改造や、トラブルが起こった時にいっつも悩んでいたので、メモしておきます。

1行で書かれているものですが、各命令に分割して解読していきます。

解読した方法

基本的にはpryを使ってそれぞれの命令を順に実行して、APIドキュメントで情報を補足していきます。

例えばこんな感じ。

pryで止まった後

page.find('table').all('tr').class
=> Array

おぉArrayか、まぁ次はmapだからArrayの要素を見た方がいいのねっていうことで

page.find('table').all('tr')[0]
#<Capybara::Element tag="tr" path="/html/body/div/div[2]/table/tr[1]">

というところまで、まぁ自分がつまづくところまでいって、そこから一度立ち止まってCapybara::Node::ElementのAPIドキュメントで次に呼ばれるメソッドがどこで定義されているのかとか他の情報を補足していきました。

解読

page

Capybara::Session が入っている。ようするにwebページの情報が入っているもの。

page.find('table')

指定のタグに該当する Capybara::Node::Element を取得するもの。なので同一ページで複数のtableタグがあると(html的に)最初のtableしかとらないんだと思う。

Capybara::Node::Element#find というメソッドが存在しささそうで、継承やらインクルードで、このメソッドがどこで定義されているかがAPIドキュメント上見えない。ただし、定数 NODE_METHODSの中にfindという単語を見かけたので、なんらかうまくしてるんだと思う。

.all('tr')

Capybara::Node::Element#allCapybara::Node::Finders で定義されている。返却されるのはArray。各要素は Capybara::Element

ここまでで、tableタグ内のすべてのtrタグ(見出しを含めた全行)を取得している。ここで行列相当の情報がとれているはず。

.map{|row| row.all('th, td')

.all('tr') 返却値の各要素は Capybara::Element。なので、trタグの中にあるthかtdを取得している(各行にアクセスできる状態になっている)

.map { |cell| cell.text.strip }

cell というのはそのもの最小単位の要素のこと。

cell.text とするとよくわからん改行やら空白やらを含んだ文字列が取得される。 なので、 String#strip で不要な空白などを削除する。String#striprubyの標準のメソッドで前後の空白を削除するメソッド。

改造するには

こちらのgist にあるように 同じページの中にtableタグが複数あってidで取得するものを限定したい時は

page.find('table' + "#" + id)

とidを追加で指定すればいいようです。

同じようにそれぞれのレベル(その中この行は省きたいとか、このcellだけを取りたい)とかある時はそれぞれのレベルの all を呼び出しているところに指定したclassとかidとかを指定しちゃうとうまくいきそうです。

すっきり!

SinatraでHTTPメソッドのDELETEを実現する

SinatraでHTTPメソッドのDELETEを実装する時かなりはまったのでメモです。実はもっと賢いやり方があるのかもしれないので、コメント大歓迎(基本的に僕のブログは全部そうです)。

今回は間違ったところから書きました。

使用環境

です。

関係しそうなもの

Sinatraのルーティング

SinatraのreadmeのRoutesの中で get、post、put、patch、delete、optionsが指定できますっぽいことが書かれています。

Sinatraの設定で関係あるもの

SinatraのreadmeのConfigration ではmethod_overrideオプションの説明があります。この記述で、「ブラウザーがDELETEメソッドとかPUTメソッドをサポートしない場合、この設定が必要になる」っぽいことが書かれています。これを読んだ時に、「まさか最新のブラウザーがDELETEメソッドとかサポートしてないはずないだろうー」と思ったのが間違いだった。

間違ったコード

上記の調べたものから「DELETEメソッドなんて新しいブラウザがサポートしてるに決まってる」と間違った解釈をして書いたコードがこちら。

hoge.rb

class HogeApp < Sinatra::Base
  get "/" do
    # Hogeの一覧を取得
  end
  
  delete "/destroy_hoge/:number" do
    # Hogeの削除処理
  end
end

index.haml

#...
%a{href: "/destroy_hoge/#{hoge.number}", 'data-method' => 'delete', rel: 'nofollow' } Destroy

この場合テストは通るけどブラウザで実際動かすとエラーになる。エラーの内容を見るとDELETEメソッドでリクエストを発行しないといけないのに、GETメソッドでリクエストが発生しているようです。

修正版

method_overrideを有効にして、WebページからのPUTとDELETE の通りに書いていきます。ようするに、formタグをの中に、nameに "_method" valueに実際送りたいHTTPメソッドを持つhiddenのinputをつけてあげるといいらしい。

hoge.rb

class HogeApp < Sinatra::Base
  enable :method_override           # ここを追加
  get "/" do
    # Hogeの一覧を取得
  end
  delete "destroy_hoge/:number" do
    # Hogeの削除処理
  end
end

index.haml

#...
%form{action: "/destroy_hoge/#{hoge.number}", method: :post, name: "destory_hoge"}
  %input{ type: 'hidden', name: '_method', value: 'DELETE' }
  %input{ type: "submit", value: "Destroy"}

おまけ

こちらの記事 link_to に :method => :delete を指定した時の動作 で 確認しましたが、railsでは link_to からリクエストを発行する時に、javascriptで書き換えを行なっているっぽいです。railsって便利〜

WebページからのPUTとDELETEの記述で「HTML5より前のHTMLでは、フォームで利用できるHTTPメソッドはGETとPOSTだけである」っていうのは間違えなのかなぁ。

Hatena Blogへ移行してみた

はてなダイアリーからHatena Blogへ移行してみました。

今まで org-mode => hatena記法(一部変換不能) => はてなダイアリーに貼り付け という手順を踏んでいました。 貼り付け後手直しが発生したり面倒だーという理由付けを自分でして、なかなか定期的にブログを書かないでいたので、ブログの移行を考えていました。

今はやりのgithub pagesjekllyなどの静的サイトの生成ツールを使ってブログを書こうかと思ったんだけど、それは僕には敷居が高すぎる。色々とサービスを探していたんだけど、最近僕の記事にはてなブックマークをつけてくれる人やtwitterで紹介してくれる方もいらっしゃってそれはそれで嬉しいので残したい。

移行が楽で、emacsから書いた文章がダイレクトにブログに投稿出来るサービスはHatena Blogだったので思い切って移行に踏み込みました。

他の方の記事でも、同様に記法の確認をしているんだけど、僕もならってmarkdownの記法がHatenaBlogでどう表現されるか確認してみます。

確認した記法

見出し記法

emacsのmarkdown-modeで書いているので見出しはこんな感じで書いてます。

emacsで C-c C-t 2
## 見出し2 ##

emacsで C-c C-t 3
### 見出し3 ###

見出し2

見出し3

リスト記法

こう書くと

- リスト1
  リスト1の中でごちょごちょ
  - リスト2
  リスト2の中でごちょごちょ

  段落をかえてごちょごちょ

こうなる

  • リスト1 リスト1の中でごちょごちょ
    • リスト2 リスト2の中でごちょごちょ

    段落をかえてごちょごちょ

シンタックスハイライト

一番使うやつ。

こう書くと

```ruby
array = [1, 2, 3]
array[0]             # => 1
array[1]             # => 2
```

こうなる

array = [1, 2, 3]
array[0]             # => 1
array[1]             # => 2

リンク

リンクも使いますねぇ。

こう書くと

[WrapExcelはここです](https://github.com/tomiacannondale/wrap_excel)

こうなる

WrapExcelはここです

他の記法

とりあえず僕が使いそうなのは上記の記法なので満足ですね。ほぼmarkdownの記法は使えるっぽいです。 詳細は はてなブログで「Markdown記法一覧」を書いてみるテスト を見てもらうとわかると思います。