【Rails】saveとsave!について

先日うちの開発メンバーから新人さんに共有があったんですが、
saveとsave!の振る舞いの違いなどについてまとめておきます。


当然のことながら、ActiveRecordでは、モデルオブジェクトの値を変更、保存、削除することができ、対応するレコードの内容を更新することができます。属性の値を変更、保存するメソッドはたくさんありますが、自分が使うところで以下一例です。

save、save!、create、create!、update、update_all、update_attribute、update_attributes、update_attributes!、destroy、destroy_all、delete、delete_all

ActiveRecord::Baseを継承したモデルオブジェクトの属性の変更について、ぽちぽちまとめてみようかなとは思いますが、とりあえず、話に上がったsaveとsave!について(´・ω・`)

まず、saveとsave!の違いは、保存出来なかった場合の振る舞いにあります。

・saveメソッドは、保存できない場合falseを返します。
・save!メソッドは、保存できない場合例外ActiveRecord::RecordInvalidが発生します。

ちなみに、どちらもバリデーションの実行をするので、バリデーションを行わない場合は、
save(:validate => false)またはsave!(:validate => false)でスキップできます。

saveとsave!は一見同じようには見えますが、falseを返すのか例外を発生させるのかの違いから、
それぞれのよく使われる利用シーンは以下のような感じです(´・ω・`)


◯DBに保存できたかどうかによって処理を分岐したい場合
大概saveが使われます。
true、falseが戻ってきてくれればさえ良いので。

@hoge = Hoge.new(:name => "piyo")
if @hoge.save
  p "ゆっくりしていってね!"
else
  p "ぬるぽ"
end

例えばこれをあえて、

@hoge = Hoge.new(:name => "piyo")
begin
  @hoge.save!
  p "ゆっくりしていってね!"
rescue
  p "ぬるぽ"
end

と書くと、処理が増えてきた時に追いかけきれなくなります(´・ω・`;)
ただ以下の場合は別です。


トランザクション中にデータを保存したい場合
こちらは大概save!が使われます。
transactionメソッドは例外が発生した場合にロールバックするので、保存に失敗したら例外を発生させなくてはいけません。

@hoge = Hoge.new(:name => "piyo")
begin
  Hoge.transaction do
    @hoge.save!
  end
  p "ゆっくりしていってね!"
rescue
  p "ぬるぽ"
end


◯コンソールからデータを操作、更新する
これはもう別にsaveでもsave!でもどっちでもかまいませんw
お好きなほうでw


あとでまとめようかなとは思いますけど、create!、update_attributes!も同様です。

【Ruby】【Mac】OSX Lion にRuby1.9.3をインストールする

OSX Lionにruby1.9.3をインストしようとすると

> rvm install 1.9.3
Installing Ruby from source to: /Users/hoge/.rvm/rubies/ruby-1.9.3-p0, this may take a while depending on your cpu(s)...

ruby-1.9.3-p0 - #fetching 
ruby-1.9.3-p0 - #extracted to /Users/hoge/.rvm/src/ruby-1.9.3-p0 (already extracted)
Fetching yaml-0.1.4.tar.gz to /Users/hoge/.rvm/archives
Extracting yaml-0.1.4.tar.gz to /Users/hoge/.rvm/src
Configuring yaml in /Users/hoge/.rvm/src/yaml-0.1.4.
Compiling yaml in /Users/hoge/.rvm/src/yaml-0.1.4.
Installing yaml to /Users/hoge/.rvm/usr
ruby-1.9.3-p0 - #configuring 
ERROR: Error running ' ./configure --prefix=/Users/hoge/.rvm/rubies/ruby-1.9.3-p0 --enable-shared --disable-install-doc --with-libyaml-dir=/Users/hoge/.rvm/usr ', please read /Users/hoge/.rvm/log/ruby-1.9.3-p0/configure.log
ERROR: There has been an error while running configure. Halting the installation.

となりインストできない。
configure.logを見ろと書いてあるので、ログを見てみると、
どうやらデフォルトのCのコンパイラが原因らしい。

なので、コンパイラにclangを使ってみる。

>rvm install 1.9.3 --with-gcc=clang

おぉいけた(´・ω・`)

とりあえず、rails3.1を使いたいので、プロジェクトをnewするためにgemsetを作ってrailsをインストしておく。

>gem install rails -y -v 3.1.0

なんとかなるもんですね。

※なんかどうもこの原因はXCODEのバージョンが原因らしいです。Xcode 4.2.1 for Lionはダメらしい。。MemberCenterからバージョンを落とすことはできますけど、それも本末転倒な感じですよね・・(´・ω・`;)

【MySQL】リモート接続できるかの確認、、とポート指定接続

別のサーバのMySQLに接続したいとき、

mysql -h xxx.xxx.xxx.xxx -u hoge -p fuga

と接続しますが、

つなげるかどうかを確認するだけならこんなコマンドもあると教えてもらったので、備忘録。

mysqladmin ping -h xxx.xxx.xxx.xxx -u hoge -p fuga

無事接続できていれば、

> mysqld is alive

と返ってきます。
おぉ。Σ(゚Д゚)スゲェ!!


ちなみに3306ポート以外で接続したい場合の指定も教えていただいたのですが、

-P NNN
--port=NNN

とのこと。

大文字のPかよ(´;ω;`)ぶわっ

【Rails】Mac(Lion)でのRails開発環境構築備忘録-その2-

前回MySQLを入れるところまでいきました。
XCODEMacPortsMySQLまでは前回分をご参照のほど(´・ω・`)

<手順アウトライン>
1. XCODEインスコ
2. MacPortsインスコ
3. MySQLインスコ

4. rvmのインスコ
5. git管理プロジェクトをclone
6. bundle install (キリッ


[4. rvmのインスコ]
rvmはいわずもがなrubyのバージョン管理ツールです。
rails3以降はbundleが優秀すぎるのでgemsetをそこまで分ける必要はないかなとも思いますが、
がちゃがちゃgemを入れまくってテストをすることも多いので、親の環境が壊れたら目も当てられないため、旧来通りgemsetを活用していきます。

>sudo port install rvm
・
・
・
== Contributions:

  Any and all contributions offered in any form, past present or future, to the
  RVM project are understood to be in complete agreement and acceptance with the
  Apache Licence v2.0.

== INSTALL:

See http://rvm.beginrescueend.com/rvm/install/

or just use:

    bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

== LICENSE:

Copyright (c) 2009-2011 Wayne E. Seguin

・
・

あ。portじゃなくてcurlだた。。

>bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

危ない危ない。とりあえず無事完了。

てか、ん・・?

・・あれ(´・ω・`;)

たしかいつもなら、

[[ -s "/Users/hoge/.rvm/scripts/rvm" ]] && source "/Users/hoge/.rvm/scripts/rvm"

bashとかzshに食わせろって言われたと思ったんだけど、、、

聞かれない・・(´・ω・`;)

なぜだ・・

とりあえずこれまで通り、.bashrcに
-s "/Users/hoge/.rvm/scripts/rvm" && source "/Users/hoge/.rvm/scripts/rvm"
を追記。

そして設定を有効にして、

>source .bashrc

rvmをいったん再起動して、

>rvm reload

手元のrubyのリストを確認し、

>rvm list known

最新版のリストを取得しておく

>rvm get head

ふう(´・ω・`)

次に、コンソールの文字化け回避のために、readlineをインストする。

rvm pkg install readline

そしてrubyインスコ。1.9.3は自分用w1.8.7はメンテナンスで必要になるまでいれないでおく。

>rvm install 1.9.2-p290
>rvm install 1.9.3-p0

確認まで

>rvm list

とりあえずメインプロジェクトのrubyのバージョンを選択

>rvm use 1.9.2-p290

gemsetを追加

>rvm gemset create hoge

設定できているかの確認。

>rvm gemset list
>rvm use 1.9.2-p290@hoge

fmfm大丈夫そう(´・ω・`)


後でリポジトリをローカルにcloneしてきたらプロジェクト毎にgemsetを切り替えるために.rvmrcをおくので、とりあえずここまで。


[5. git管理プロジェクトをclone]
ここからはGitです。
仕事ではチームで開発することが多いため、ソース管理はgitを使っています。
また、個人用のソース管理もSubVersionではなくGit管理にしているので、これまでのソースコードを手当たり次第cloneしていきます。

>git clone git@~~~~~~~~~ hoge

ちゃんとcloneできているか確認

>cd hoge
>git fetch origin
>git config --global user.name "Your Name"
>git config --global user.email you@example.com
>git pull origin master

よし。大丈夫だ。問題ない。

そして先ほど保留にしていた.rvmrcをセット

>echo "rvm use 1.9.2-p290@hoge" > .rvmrc 

.rvmrcの設定を有効にするには、もう一度同じディレクトリを踏めば良いので、

>cd .
==============================================================================
= NOTICE                                                                     =
==============================================================================
= RVM has encountered a new or modified .rvmrc file in the current directory =
= This is a shell script and therefore may contain any shell commands.       =
=                                                                            =
= Examine the contents of this file carefully to be sure the contents are    =
= safe before trusting it! ( Choose v[iew] below to view the contents )      =
==============================================================================

となるので、

>yes

としましょう。

.rvmrcの内容を確認する必要があれば

>view

とすると、設定内容を確認できます。

そして改めて

>yes

これでhogeディレクトリに入った時に、自動で1.9.2-p290のgemset hogeを利用するようになりました(´・ω・`)


[6. bundle install]
最後に、railsプロジェクトの初期設定を。

>bundle install
>rake db:create
>rake db:migrate
>rake db:seed
>rake spec
>rails s

よしよし問題なしんぐ(´・ω・`)

【Rails】Mac(Lion)でのRails開発環境構築備忘録-その1-

先日手元のwindowsマシンの電源ユニットが逝ったため、念願のMacBookAirを購入しました(・ω・´)

家や仕事では開発環境はMacなのですが、これまで移動中などにちょっとコードを触りたいときには、
windowsに乗せたVMUbuntuか、
iphoneもしくはipadからリモートサーバにsshつないでちょっと試すという、
とてもとてもめんどくさいことをしていたので・・いやもうホント、買い替えは願ったりかなったりでしたw(´・ω・`)

ちなみに、iphoneipadからのssh接続は、私はpromptを使わせていただいています。

ターミナルアプリはいろいろ試しましたが、promptは鍵認証や、tab補完、入力したコマンドの履歴を残していてくれたりと、使い勝手良いですwちゃんとvimも使えますし。


ではでは、しばらく環境構築をしておらず少しはまったところもあるので、まっさらのMacBookAirへのRails環境構築の備忘録を残しておきます。



ちなみに、自分の場合、なので、参考になるかどうかは正直わかりませんw
Railsの環境(前提)は
MySQLを利用
・Rails3以上のプロジェクト
・画像処理を扱うのでImagemagick必須(´・ω・`;)
というところです。
あとは、Rails以外にもObjective-Cを触るという程度でしょうか。

<手順アウトライン>
1. XCODEインスコ
2. MacPortsインスコ
3. MySQLインスコ
4. rvmのインスコ
5. git管理プロジェクトをclone
6. bundle install (キリッ


[1. XCODE]
まず、何はともあれこのコがいないと何も始まらないのでインストールします。
本体はAppStoreからb(もちろん無料ですw)
てか、最近はXCODEもAppStore経由になっているのね(´・ω・`;)

ここは特に問題ないはず。


[2. MacPortsインスコ]
XCODEに続きこのコもいないと話が始まらないのでインストールします。
本体は本家(http://www.macports.org/install.php)から。
とりあえずpkg版で問題ないので、上のページの中段くらいに、
 Mac OS X Package (.pkg) Installer
とあるので、その中から自分の環境にあったpkgをダウンロードします。

このコも特に問題なくインストできるはずです。

また、最新の状態に更新したければ

sudo port selfupdate

しておきましょう。apt-get update と同じ意味です。


[3. MySQLインスコ]
さて、ここからUbuntu等と違う部分です。
まず、MacPortsから入れるMySQL5.x系はmysql5コマンドで起動します。
mysqlではありません。。煩わしい(ボソッ(´・ω・`)
そして、自動起動設定やソケットなどハマるポイントがいくつか見事に用意されていますw

ではでは。
まずは、mysql5系のパッケージを確認します。

>port search mysql5
mysql5 @5.1.59 (databases)
    Multithreaded SQL database server

mysql5-devel @5.5.2-m2 (databases)
    Multithreaded SQL database server

mysql5-server @5.1.59 (databases)
    Multithreaded SQL database server

mysql5-server-devel @5.5.2-m2 (databases)
    Multithreaded SQL database server

fmfm(´・ω・`)

今回は、mysql5-serverを利用します

>sudo port install mysql5-server

だーっとインストが走って、しばらくすると、

--->  Fetching mysql5-server
--->  Verifying checksum(s) for mysql5-server
--->  Extracting mysql5-server
--->  Configuring mysql5-server
--->  Building mysql5-server
--->  Staging mysql5-server into destroot
--->  Creating launchd control script
###########################################################
# A startup item has been generated that will aid in
# starting mysql5-server with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load mysql5-server
###########################################################
--->  Installing mysql5-server @5.1.59_0

In order to setup the database, you might want to run
sudo -u _mysql mysql_install_db5
if this is a new install

---> Activating mysql5-server @5.1.59_0
---> Cleaning mysql5-server

と返してきますので、言われたとおりにしましょう。

MySQL5の自動起動設定など

>sudo port load mysql5-server
>sudo -u _mysql mysql_install_db5

そして、ハマリポイントのソケットエラー。

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/opt/local/var/run/mysql5/mysqld.sock' (2)

といっても、socketがないといっているだけなので設定ファイルを食わせるだけです。

>sudo launchctl unload -w /Library/LaunchDaemons/org.macports.mysql5.plist
>sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist

これで、

>mysql5

で起動するはずです。
それでもエラーが直らなかったら、/opt/local/var/run/mysql5 の所有者がuserなどになっている可能性が高いので、華麗にchownしてください(´・ω・`)

※ちなみにrootパスは自己責任で設定してくださいねw

【Rails】画像ファイルの相対パスと絶対パス【画像が見えない(´・ω・`;)】

先日、
クライアント側でJavaScriptを実行する時にちょぴっとハマった現象。

サンプルではJQueryを使っていますが、JavaScriptでも全く同じ(´・ω・`;)

[route.rb]

resources :hoges do
get :fuga, :on => :collection
end

[View(というかhtml)]

<html>
<head>
<title>yukkurisan</title>
<script src="/javascripts/jquery-1.6.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
  $("div.hoge").eq(0).css("background-image", "url('../images/yukkuri.png')");
});
</script>
</head>
<body>

<div class="hoge">&nbsp;</div>   ・・・・(1)

</body>
</html>


要は、htmlがロードされたら(1)の背景にyukkuri.pngが読み込まれるというだけの処理。


テストしていた時には、

**yukkuri**/hoges/fuga

といつもの通りアクセスし、プログラムに問題がないことを確認していたのですが、
とある静的リンクからたどってきた場合に「画像だけが表示されない」現象に遭遇(´・ω・`;)


なんのせいかすぐはわからなかったのですが、じきに解決。



その理由はというと、、


叩かれたURLが、

**yukkurisan**/hoges/fuga/

となっていたことが原因でした・・orz


つまりです、

この場合、railsにしてみれば、

**yukkurisan**/hoges/fuga も
**yukkurisan**/hoges/fuga/ も

同じルーティングを指すのですが、

viewに書かれたjavascriptからすると、このルーティングの違いはとても重要で、
特に相対パスの場合は、「今いる階層からみたパス」なので、/ のあるなしは非常に重要なのです。。


../images/yukkuri.png は、
一つ上の階層のimagesディレクトリの(ry
というわけなので、

/ なしの場合、hogesの並びのimagesディレクトリ以下のyukkuri.png
/ ありの場合、fugaの並びのimagesディレクトリ以下のyukkuri.png

を指すという・・



結局、ここにくるための静的リンクは変えられないし、
内部的にもfuga_hoges_pathといたるところで使っているので、
仕方がないので、相対パスはやめて、
/images/yukkuri.png
と全て絶対パスに置換しました。。・x・;


ま。最初から絶対パスにしておけば何事もなかったんですがね・・

【Rails】sizeとcountとlengthについて

古いソースを眺めていて気づいたことなんだけど、いや、うん、というかちょっとこれは・・と思う書き方があったので。

class Yukkuri < ActiveRecord::Base
end

と、いつものモデルがあって、

このモデルのyukkurisテーブルの全レコード数を確認しようとして、だとは思うんだけど、

p Yukkuri.all.size

なるコードが書いてあった・・・



いやぁこれはないなと。



これって要は、掃き出されるSQLを見るとよくわかるけど、
こう書いた場合のsizeメソッドは、配列のサイズを取得するだけなので、select count(*) from yukkuris 〜 といったSQLは発行されず、(きっと?)期待しているメソッドチェーンにはならない。

@yukkuri = Yukkuri.all
p @yukkuri.size

と書くのとまったく一緒で、yukkuris テーブルの全レコードを取得して、Yukkuriオブジェクトの配列を作り、その配列のsizeを調べている。というだけなんですよね。


これをやりたいなら、

Yukkuri.count

これだけ。

これで発行されるSQL

select count(*) from yukkuris 〜

となる。


書き方だけ見ると一見同じように見えるけど、レコード数が多くなるほどレスポンスが目に見えて違ってくるんですよね。





てなわけで、本題。

いろんなサイトでもsizeとcountとlengthの違いについては記載されていますが、改めてその違いについて。



まず、3者ともrubyのArrayクラス、Hashクラスのメソッドとして使えます。(他にも色んなとこで出てきます・・)

なので、

[1, 2, 3, 4, 5].size

とか

{:hoge => 1, :fuga => 2}.length

とか

[1, 2].count

などのように使います。
違いはまぁありませんね。


次に、たとえばrubyのFixnumクラスやBignumクラスはsizeメソッドのみで、lengthやcountは使えません。

Fixnumの場合だと、これは31ビットまたは63ビットの固定長整数を扱うクラスなので、
sizeメソッドが返す値は、何のマシン上で実行してるかにより結果は変わりますし(変わるよね?)、正直自分はこの用途で利用したことはありませんw

たとえば、32bitマシンなら

123.size
=> 4

みたいな感じですね。


Stringクラスでは、size、lengthは使えますが、countは単体では使えません。
文字数を数えるメソッドですね。

"123aaabbb".size

12345.to_s.length

と使え、

"hoge".count

はエラーになります。
ただ、countは引数を受けて、そのオブジェクト内に含まれる引数で指定した文字列の文字数を返すので

"yukkuri".count("u")
=> 2

となり、有効っちゃ有効です。


あと、上の例のようにモデルのテーブルにどのくらいのレコードがあるかを調べる場合、countが使えますが、sizeやlengthは使えません。
Active Recordの集計関数として、countは定義されていますが、sizeやlengthは定義されていないためです。

Yukkuri.count

は有効ですが、

Yukkuri.size

はエラーになります。

同じく集計関数として定義されているものは、
おなじみの、average や sum、maximum などです。

Yukkuri.maximum(:age)

みたいな感じ。


また、モデル間でhas_manyのアソシエーションを組んでいる場合はもうちょっと複雑で、
特にsizeメソッドはカウンタキャッシュを使っている場合(belongs_to に :counter_cache=> true と仕込むアレ。)と使っていない場合で挙動が違います。
さらに、sizeもlengthもオブジェクトがロード済が否かでも挙動が違います・・・
カウンタキャッシュを使っていない場合、
sizeは、ロード時には配列の要素数を返し、未ロード時にはcount文を発行しますし、
lengthは、ロード時には配列の要素数を返しますが、未ロード時にはselect * 〜を発行したのち配列の要素数を返します。
そんでもって、
countはどんなときであれ毎回count文を発行します。

動きは一見同じように見えるけど、どの時にどのメソッドを選択するかで動きが変わり、それによって速度がだいぶかわるってわけです。
countが毎回良いわけでもなく、lengthが毎回良いわけでもない。という(´・ω・`;)



細かいこといえばまだまだ足りないですが、それはまた機会があったら。




ま。



なんというか。



Railsは似たような意味だけど厳密には違うっていうメソッド山ほどありますよね・・



っていう。。