acts_as_paranoid + ユニークインデックス

Railsで論理削除を行うときはacts_as_paranoidを使用するのが一般的です。
Model側でvalidates_uniqueness_of_without_deletedを使用するとユニーク制限をかけることができますが、DB側でユニーク制限をかけようとすると問題が発生します。

  # schema.rb
  create_table "customers", :force => true do |t|
    t.string   "code", :null => false
    t.datetime "deleted_at"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
  add_index "customers", ["code", "deleted_at"], :name => "customers_idx01", :unique => true

一見正しそうですがnullはユニーク制限の対象外となり次のようなレコードが入ってしまいます。

|code|deleted_at|
|001| NULL|
|001| NULL|

こういう時はindexにwhere条件を指定して直接DBに設定しましょう。

class AddIndexCodeToCustomers < ActiveRecord::Migration
  def self.up
    sql = 'create unique index customers_idx01 on customers (code) where deleted_at is null;'
    ActiveRecord::Base.connection.execute(sql)
  end

  def self.down
    remove_index :customers, :name => :customers_idx01
  end
end

procedureを使用してDBの操作を行うとRailsのModelを経由しないのでバリデーションが行われません。そういった場合はDB側でもチェックを行いたいですね。

CoffeeScriptオンリーでWebアプリを作ってみた

この記事は大都会岡山 Advent Calendar 2012向けに書いたものです。

昨日の@ore_publicさんの記事は読みましたか!?さすが僕らのリーダーやで。

さて私、RailsからRubyを始めたワリにはCSSとかJavaScriptとか苦手で、あまり関わらないように生きてきました。しかし、CoffeeScriptの登場で大量のfunctionや"{}"や";"を使わずともJavaScriptを書くことができる時代になりました。ステキ。せっかく時代がこちらへ歩み寄ってきてるのだから、なにか作ってみることにしました。

お題はLuckOfWiseさんが会社の飲み会用に作ってたAndroidアプリを真似てみることに。車輪の再発明はムダじゃない!モチベーションが大切です。

できた。
http://tomikuji.herokuapp.comで使うことができます。ソースはgithubに置いてます。
オータムジャンボ宝くじの番号を入力すると当たりの可能性が表示されます。会社で共同購入したくじをみんなで開封するときに使うと楽しいです。

Rails

普通にRails new tomikujiして空のcontrollerを作っただけです。今回は動作確認やCoffeeScriptコンパイルするのがRailsの役目です。
コントローラとビューはこんな感じ

├── controllers
│   ├── application_controller.rb
│   └── main_controller.rb
└── views
    ├── layouts
    │   └── application.html.erb
    └── main
          └── index.html.erb

CoffeeScript

前方一致で当たりの可能性を表示するロジックはちょっと工夫してみました。

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

$(document).ready ->
  if Modernizr.touch
    $(".key").bind 'touchend', (event) ->
      btnClick($(@).text())
  else
    $(".key").click ->
      btnClick($(@).text())

  setTimeout("scrollTo(0,1)",100)

# 当たり番号
hits =
  "1等" : "187077"
  "2等" : "179409"
  "3等" : "119658"
  "4等" : "....30"
  "5等" : ".....9"

# 表示処理
btnClick = (number) ->
  if number == "clear"
    $("#number").text("")
    $("#message").text("")
  else
    score = $("#number").text() + number
    if score.length <= 6
      $("#number").text(score)
      showResult(score)

# 結果表示
showResult = (score) ->
  message = ""
  for key, value of hits
    diff = value.slice(0, score.length)
    if score.match(new RegExp("^#{diff}"))
      message = message + key
  if message == ""
    $("#message").text("残念!")
  else
    $("#message").text("#{message}の可能性があります")

初めはスマートフォン用もclickイベントを使ってたんですが、処理速度がメチャクチャ遅かったんですよね。なので、touchイベントに変えました。clickイベントを使うかtouchイベントを使うかの判断はModernizrを使ってみました。

css

普通のブラウザ用とスマホ用の2つを用意して画面の幅によって切り替えてみました。レスポンシブデザインとかやってみたかったの。

<head>
  <title>富くじ</title>
  <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/base/base-min.css">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
  <%= stylesheet_link_tag    "application", :media => "screen and (min-width: 641px)" %>
  <%= stylesheet_link_tag    "application_small", :media => "screen and (max-width: 640px)" %>
  <%= javascript_include_tag "application" %>
  <%= javascript_include_tag "http://modernizr.com/downloads/modernizr.js" %>
  <%= csrf_meta_tags %>
</head>

通常は複数のscssファイルが1つに統合されてしまうのですがマニフェストファイルを編集して2つに分けています。

stylesheets/
├── application.css
├── application_small.css
├── main.css.scss
└── small.css.scss

app/assets/stylesheets/application.cssにmain.css.scssを読み込むように設定

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the top of the
 * compiled file, but it's generally better to create a new file per style scope.
 *
 *= require_self
 *= require main.css.scss
 */

app/assets/stylesheets/application_small.cssにsmall.css.scssを読み込むように設定

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the top of the
 * compiled file, but it's generally better to create a new file per style scope.
 *
 *= require_self
 *= require small.css.scss
 */

あとはconfig/environments/production.rbに明示的にプリコンパイルするcssファイルを記述します。

  # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
  config.assets.precompile += %w( application_small.css )

こんな感じでCoffeeScriptを使ってWebアプリを作ることができました。ちょっとしたアプリならクライアントだけで実現できるのがいいですね。
さて、明日はハカセのアイコンでお馴染みのにゃーさんです。お楽しみに!

irbやrails cの履歴が表示されなくなった時の対処方法

先日rvmからrbenvに変更したのですが、irbの履歴が出てこなくなりました。いや、正確には出てくるのですが、一度終了してしまうと、以前の履歴が表示されないのです。当然rails cも同じで不便な思いをしていました。

Twitterでつぶやくと@nysalorさんが対応方法を教えてくれたのでその方法を紹介します。

IRB.confの確認

irbを起動して次のコマンドを入力します。

> IRB.conf

CONF[:SAVE_HISTORY]の値の確認

CONF[:SAVE_HISTORY]の値がnilになっている場合、履歴は表示されません。

.irbrcの編集

ホームディレクトリに.irbrcを作成し次を記述します。

IRB.conf[:SAVE_HISTORY] = 1000


irbrails cを起動してCONF[:SAVE_HISTORY]の値が設定した数値になっていることを確認して下さい。
今度は一度終了しても以前の履歴を覚えてくれてると思います。

Java Preferencesが消えた世界でいかにしてJavaのバージョンを切り替えるか

(2012.11.20 追記)
aoetkさんからコメントでご指摘いただきました。Javaコントロールパネルからpathをコピーする方法はApple的に推奨されていないようです。
http://developer.apple.com/library/mac/#qa/qa1170/_index.html

/usr/libexec/java_homeコマンドがpathを返すようなので、その実行結果をJAVA_HOMEに設定します。

.bash_profileか.bashrcにJAVA_HOMEを設定

export JAVA_HOME=`/usr/libexec/java_home`
export JAVA=$JAVA_HOME/bin




↓↓↓ここから元記事↓↓↓
いつの間にやらMacOS XからJava Preferencesが消失してしまいました。
Java言語で開発を行うことはないのですがJenkinsはできれば1.7系で動かしたいのです。
さてどうやって切り替えようかと思っていたのですが会社でpatorashさんが良い方法を教えてくれました。

Javaのpathを探す

システム環境設定→その他→Javaを起動する

JavaコントロールパネルのJavaタブ→表示をクリック

パスの内容をコピー

.bash_profileか.bashrcにJAVA_HOMEを設定

JAVA_HOMEはさっきコピーした内容をそのままペーストするのではなく、最後の/bin/javaは取り除きます。
例えば次のような内容であれば
/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java
次のようになります。

export JAVA_HOME="/Library/Internet Plug-ins/JavaAppletPlugin.plugin/Contents/Home"
export JAVA=$JAVA_HOME/bin

切り替わったか試してみましょう。

$ source .bash_profile
$ java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

OK!良い感じです。

rbenv rehashを行わなくて良い方法

rvmがオワコン扱いされだしたのはいつの頃からでしょうか?私はrbenvのrehashが嫌でしつこくrvmを使い続けていたのですが、ここ最近のrvmでのrubyインストールの不安定さに嫌気が差してrbenvに乗り換えることにしました。

$ cd
$ rm -rf .rvm
$ brew install rbenv$ brew install ruby-build
$ echo 'eval "$(rbenv init -)"' >> .bash_profile
$ brew install readline
$ rbenv install 1.9.3-p194
$ rbenv global 1.9.3-p194

ちょとirbで動作確認してみます。

a = 1
a = 2
# ↑キーを押してキーヒストリが効くことを確認。readlineがうまく組み込まれてないとヒストリが効かない
require 'psych' #yamlパーサーの確認。rvmはここで詰まることがよくある
require 'zlib' #同じくrvmはここで詰まることがよくある
require 'openssl' #同じく(略)

私の環境では問題ありませんでした。
次にrbenv で gem を使った時に rbenv rehash しなくて良くするというエントリを参考にしてシェルスクリプトで関数定義を行い、gemおよびbundleコマンドのオーバーライドを行いました。私のPCはMacOS Xなんですがrehashコマンドは無いみたいなので参考元のスクリプトからコピーする際に省いています。

$ vi .bash_profile
#適当な場所に定義
function gem(){
  $HOME/.rbenv/shims/gem $*
  if [ "$1" = "install" ] || [ "$1" = "i" ] || [ "$1" = "uninstall" ] || [ "$1" = "uni" ]
  then
    rbenv rehash
  fi
}

function bundle(){
  $HOME/.rbenv/shims/bundle $*
  if [ "$1" = "install" ] || [ "$1" = "update" ]
  then
    rbenv rehash
  fi
}

では試してみましょう。

$ rspec
rbenv: rspec: command not found # rspecコマンドは見つからない
$ gem install rspec
$ rspec
/Users/kazuhisa/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.12.0/lib/rspec/core/configuration.rb:784:in `load': cannot load such file -- /Users/kazuhisa/spec (LoadError)
#rspecを実行してテストファイルが見つからないのでエラーになってる

自動的にrbenv rehashしてくれてるみたいですね。良い感じです。
しばらくはrbenvを使ってみようと思います。

Scientific Linux release 6.3にQt4.8.xをインストールする

2012年11月9日にcapybara-webkitの0.13.0がリリースされました。このバージョンからQt4.6.x系ではコンパイルに失敗してしまうようです。現在テスト用サーバーとして使っているScientific Linux release 6.3にはyumでは4.6.x系までしかインストールできないようなのでソースを持ってきてコンパイルしてみました。

ダウンロードサイトはこちら。
http://qt-project.org/downloads

次の手順でインストールしました。

$ wget http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz
$ tar zxvf qt-everywhere-opensource-src-4.8.3.tar.gz 
$ cd qt-everywhere-opensource-src-4.8.3
$ ./configure
Which edition of Qt do you want to use ?

Type 'c' if you want to use the Commercial Edition.
Type 'o' if you want to use the Open Source Edition.
o [Enter]

This is the  Open Source Edition.

You are licensed to use this software under the terms of
the Lesser GNU General Public License (LGPL) versions 2.1.
You are also licensed to use this software under the terms of
the GNU General Public License (GPL) versions 3.

Type '3' to view the GNU General Public License version 3.
Type 'L' to view the Lesser GNU General Public License version 2.1.
Type 'yes' to accept this license offer.
Type 'no' to decline this license offer.

yes [Enter]

$ gmake
$ sudo gmake install

最後に.bashrcにpathを追加しました。

$ vi ~/.bashrc

PATH=/usr/local/Trolltech/Qt-4.8.3/bin:$PATH:$HOME/.rvm/bin # Add RVM to PATH for scripting

MacOSXと比較してLinuxのcapybara-webkitは動作が非常に遅いのですが、新バージョンで改善してるといいな。

半分ジョーク、半分マジ ken_allをRubygemsで公開しました

今年の夏、FizzBuzz 問題どや顔で解くひとなんかよりも "KEN_ALL.csv" をうまく扱える人の方が社会的貢献度高いという話題がtwitterで盛り上がったのを覚えていますでしょうか?私もそのときは「そんなんあったなぁ。懐かしい」と思っていたのですが、秋になってから隣の席の人がKEN_ALL.CSVの扱いでキレて頭を悩ませてるのをみて、社会的貢献をしようと思い立ったわけです。

...嘘です。Rails Engineを使ったgemを作りたいなぁと思っていたところにネタが転がってきただけです。手段のためには目的は選ばないのです。

ken_all | RubyGems.org | your community gem host

使い方

Railsで簡単に最新の郵便番号情報をネットから取り込むことができます。
1.Gemfileに追加

gem "ken_all"

2.プロジェクトのディレクトリでrakeタスクを実行

$ rake ken_all:install:migrations #migrationファイルをプロジェクトに作成
$ rake db:migrate #テーブルの作成
$ rake ken_all:import #郵便番号の取込

取り込んだデータはKenAll::PostalCodeモデルで使用することができます。
詳細はGithubのREADMEを見て下さい。

Rails Engineについて

Rails EngineとはRailsで作成したアプリケーションの一部を他のアプリケーションに取り込むことができる機能です。Rails用ページネーションとして有名なKaminariRails Engineを使用して作られているそうです。
Engineで私が気に入った点はテストのしやすさです。
開発中のEngineはdummyディレクトリ以下に作成されているRailsプロジェクトでいつでも実行することができます。rspec(※)も通常のRailsプロジェクトでやってる通りに書くことができます。

Rails Engineでtest unitではなくrspecを使う方法はwhile(false){.net}: Testing Rails Engines With Rspecの手順で行けます

KEN_ALLの不思議な魅力

KEN_ALL.CSVは業務システムを構築する人は一度は通る道だと思います。本家「日本郵便」が配信しているデータにもかかわらず仕様があやふやなまま何度も手直しされて今に至っているのが、データを軽く眺めるだけでも理解できます。具体的には郵便番号データの落とし穴郵便番号データの落とし穴」に落ちてしまいました。に詳しく解説されているとおりです。
おそらくパンチャーの方が汎用機に向かって一つ一つ手入力されているのでしょう。長い住所を途中で改行し複数行のデータを作るのはとても大変な作業だとお察しします。
そう、無機質であるはずのデータの向こうに人のぬくもりを感じるのです!
これって素晴らしいことだと思いませんか?

しかし、現実的には良い感じに手直しした郵便番号データをzipcloudさんが提供してくださってるのでそちらを使うののが正しいです。

ただ、データを眺めるうちに私は何とかしてプログラム処理のみで整形できないか試してみたくなったのです。
具体的にはzipcloudさんのページに書かれている解説の通りの処理を行なっているのですが次の部分だけは処理することができませんでした。

・町域名で、丸括弧で囲まれている部分を除去
 ※町域名の文字数が多いために複数行に分割されてしまっている場合は、1行にマージしておいてから丸括弧を除去します。
 ※括弧内の文字が「地名」や「ビルの階」など、住所として使えそうなものは町域名の末尾に追加します。
 ※括弧内に地名が複数列挙されている場合は複数行に分割します。

郵便番号データの町域名のカッコの扱いは非常に曖昧で、色々な使われ方をしています。何とかルールを見出してプログラムで処理したかったのですがうまくいきませんでした。
なので、ken_allのgemも外部ファイルを取り込む機能を使ってzipcloudさんのデータをimportするのが現実的かなと思っています。

$ rake ken_all:import:file FILE=/path/to/x-ken-all.csv #外部のファイルから取り込みます

まとめ

  • Rails Engine便利。みんなプロジェクトで作ってる部品や仕組みを手軽に公開しよう。
  • 郵便番号データは素直にzipcloudさんで公開されているものを使おう。
  • KEN_ALL.CSV謎深い。でも魅力的。