if文をずらずら書かないために

rubyだけじゃないんだけど。
変数の値を繰り替えしif文の条件にする場合

1
2
3
if crud == 'insert' || crud == 'update' || crud == 'delete'
  #do somtehing
end

だったらこう書いた方がいいよね。

1
2
3
if ['insert','update','delete'].include?(crud)
  #do somtehing
end

他に良い案ありますか?


watchコマンド~ログとかファイル監視する時とかは何も考えずにtail -f ファイル名としちゃう人へ

ログとかファイル監視する時とかは何も考えずに

tail -f debugger.txt

とかってしちゃう人いると思うんですが。

表示する内容が少ない場合なら、watchってのがある。

watch cat debugger.txt

でデフォルトの2秒起きにcat debugger.txtが実行されます(表示は画面に収まるまで)

秒数変えたければ(下記例は60秒)

watch -n 60 cat debugger.txt

コマンドに-dをつければ変更箇所を毎回ハイライトしてくれる。

watch -d cat debugger.txt

cumulativeを指定すれば、watchし始めてからの変更をハイライトしてくれる。

watch -d=cumulative cat test.txt

もちろんコマンドも可能

watch -d ls -la

ロックファイルとか、ファイルが作成されたことの確認したい時とかに便利かもしれません。

開発中にログファイルに特定の文言が出たか知りたい場合とかにも便利。
例えば「error」と「fatal」がログに出た場合に、watch使えばすぐ気づける。

watch grep -n -e error -e fatal app.log

ってやると行番号付きでerrorかfatalが発生したことがわかる。


コマンドの繋ぎ方

コマンドの繋ぎ方は

パイプ(|)とかファイルに書き込むリダイレクション( > もしくは > > もしくは <)が有名ですが。

ps aux | grep postfix

他にもあるんですね。

下の例のコマンド自体は意味のないコマンドですが。

1 cd /var/www/app/log/ ; scp app.log 192.168.X.XX:/home/user
2 cd /var/www/app/log/ && scp app.log 192.168.X.XX:/home/user
3 cd /var/www/app/log/ || scp app.log 192.168.X.XX:/home/user

1がコマンドを実行した後に次のコマンドを実行する方法。
2が移動に成功した場合のみ次のコマンドを実行する方法。
3が移動に失敗した場合のみ次のコマンドを実行する方法。

cronで指定するコマンドや、postfixのエイリアスで標準入力にメールの内容渡してプログラムを起動する際に使ったり。

例えばrailsのスクリプトランナー使いたい場合に。

cd rails appのルートディレクトリ ; rails runner "Calender.generated" RAILS_ENV=production

ルートに移動してからスクリプトランナー起動。
とか。


Ubuntu11.10にアップグレードしたらwi-fiが遅い件

珍しく休日にPCを起動した。
何年ぶりだろう。

自宅PCはWindows7とUbuntuのデュアルブートにしているのだけど、Ubuntuを11.04から11.10にしたら無線が遅い。
ネットには接続されるし、IPもふられてるんだけど通信速度が異常に出ない。
なんじゃこりゃ。

このフォーラムWireless Internet Connection so slow after upgrade to 11.10のとおりにコマンド打ったら、解決した。

sudo rmmod iwlagn
sudo modprobe iwlagn 11n_disable=1

SDカードをマウントして、ファイル削除したけどSDカードの容量が増えない場合

注意)以下の作業はやる前に、バックアップを取ってからやってください。

Ubuntu(OS関係あるのか不明)で、Android(IS04)のSDカードをマウントして、ファイル削除したけどSDカードの容量が増えない。゚ヽ(゚´Д`)ノ゚
よく見ると

.Trash-1000

とかってゴミ箱的なフォルダがあって、そこに削除したファイルが移動してた。
んでそこのフォルダ内のファイル全部選択して削除したけど、ファイルが一向に消えないヽ( ̄д ̄;)ノ

ここ見て、ubuntuのゴミ箱を全て空にしたら消えた(´・ω・`)
http://www.google.com/support/forum/p/android/thread?tid=60747a992c0a473b&hl=en

そして再び携帯に戻ったら、破損したSDカードとか出るようになってフォーマットしますかの一択となった。
色々やってたから、なんかまずいの消したのか?
それともまさか上の操作がNGなんだろうか?

めんどいので調査しないけど。

っていう投げっぱなしのどうしようもない記事。


指定した要素で指定した数分、配列を埋める

初期値を指定して配列を作りたいならこうできる。

1
2
  Array.new(3, nil) # => [nil, nil, nil]
  Array.new(2, "foo") # => ["foo", "foo"]

んで、指定した値で要素数Nの配列を要素数Mにしたい場合。

1
  変数.fill(指定した値,変数.size..(N-1))

で出来る。

例)とりあえずMを5とする場合。

1
2
3
4
5
6
7
8
  ary = [1]
  ary.fill(nil,ary.size..4) #=> [1, nil, nil, nil, nil]

  ary = [1,2]
  ary.fill("empty",a.size..4) #=> [1, 2, "empty", "empty", "empty"]

  ary = [1,2,3,4,5]
  ary.fill(nil,a.size..4) # => [1, 2, 3, 4, 5] 

ちなみにfillは!ついてないけど破壊的メソッドなので、レシーバの値変わってしまうのがちょっと注意。

1
2
3
  ary = [1]
  ary.fill(nil,ary.size..4) #=> [1, nil, nil, nil, nil]
  p ary #=> [1, nil, nil, nil, nil]

pusherの助けを借りたWebSocketを使ったチャットシステム

リアルタイムなchatを作りたい。
そんな欲求誰にでもあるものです。

時間のない主婦の方にも手軽にパパっとチャットシステムを作る方法を紹介しようと思います。

材料は
rails
pusher

作る料理は
チャットシステム(現在の参加者表示機能付き)

の二つです。

変なテンションな書き方はここまで。

今日の記事は全面的にここを参考にさせていだきました。
Yuno

pusherはWebSocketsを利用したAPIです。

Leader in realtime technologies| Pusher

わかりやすい図はっときます。

f:id:gogo  _sakura:20120124124957p:image

例えばブラウザがpostリクエスト投げると、それを受け取ったサーバからpusherにそのデータを加工してパス。
するとブラウザにpushしてくれるというイメージ。

pusher利用にはユーザ登録で得られるAPI Credentialsが必要です。
まずはpusherのサイトでユーザ登録して、

app _id
key
secret

をゲットしてください。

今日の記事は実際に作ったものからコピペで書いていますが。
view部分はhamlで書いてるので、見ずらくてすいません。
erbメインの方は脳内で変換かけてください。

とりあえずGemfileに記述

1
gem 'pusher'

bundle install後、config/initializers内にpusher.rb作成

1
2
3
  Pusher.app_id = ユーザ登録でゲットしたapp_id
  Pusher.key = ユーザ登録でゲットしたkey
  Pusher.secret = ユーザ登録でゲットしたsecret

config/route.rbにauthアクションへのルーティング追加

1
2
3
resources :chats , :only => [:index , :create , :destroy] do
  post :auth , :on => :collection
end

現在の参加者を取得するためには、pusherのPresence Channelsを使用しなければなりません。
Presence Channelsを使用するためには、認証を受けること・channel名のprefixにpresence-つけないといけません。

view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
= javascript_include_tag "http://js.pusher.com/1.11/pusher.min.js"

:javascript
  $(function(){
    Pusher.channel_auth_endpoint = '/chats/auth';
    var pusher  = new Pusher("#{ユーザ登録でゲットしたkey }");
    var channel = pusher.subscribe("presence-chat");
    channel.bind("chatevent", function(html) {
      $("#chat_body").val('');
      $("ul").prepend(html);
    });

    channel.bind('pusher:subscription_succeeded', function(members){
      members.each(add_member);
    });

    channel.bind('pusher:member_added', function(member){
      add_member(member);
    })

    channel.bind('pusher:member_removed', function(member){
      remove_member(member);
    })
  });

  function add_member(member) {
    var container = $("<span>", {
      "class": "member",
      id: "presence_" + member.id
    });

    $('.members').append(container.html(member.info.name + " "))
  }

  function remove_member(member) {
    $('#presence_' + member.id).remove();
  }

= render "form"

%div
  現在の参加者
  %span.members

%ul#chat_list
  = render partial: "list", locals: { :chats => @chats }

フォームとリストの部分テンプレートは割愛。

認証は

1
Pusher.channel_auth_endpoint = '/chats/auth';

で指定したエンドポイントにコールバックされます。

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class ChatsController < ApplicationController
  protect_from_forgery :except => :auth

  def create
    @new_post = Chat.new(params[:chat])
    @new_post.user = @current_user

    if @new_post.save
      Pusher["presence-chat"].trigger!(
        "chatevent",
        render_to_string(
          file:   "chats/_list.html.haml",
          layout: false,
          locals: { :chats => Array.wrap(@new_post) }
        )
      )
    end

    render 'index'
  end

  def auth
    if current_user
      auth = Pusher["presence-chat"].authenticate(params[:socket_id],{
          :user_id => @current_user.id,
          :user_info => {
            :name => @current_user.name
          }
        }
      )
      render :json => auth
    else
      render :text => "Not authorized", :status => '403'
    end
  end

end

endpointで設定したauthアクションでは、コールバックされた際に渡されるsocket _idを引数authenticateを実行しています。
ちなみに自分はこの認証ではまりました。

1
protect_from_forgery :except => :auth

でコールバックで呼び出された際に、CSRFを無効にしてあげる必要があったんですねぇ。

以上で多分動くはずです。


同一の操作から呼ばれたかオブザーバで判定する方法

railsのobserverでユーザ情報とプロフィール情報を監視していて、
例えば1つのフォームにユーザ情報とプロフィール情報の編集項目があった場合。

after saveでフックした際には
after
saveは2回。それぞれのモデルで呼ばれる。

んでその二つ呼ばれたafter _saveが同じ処理で呼ばれたのかってわかんないと思っていた。

先日入れたuserstampの処理がどうなっているのだろうかと見たら、
ActionControllerのbefore _filterでThread.currentにuserのIDを押し込んでいた。

実際のソースは以下のとおり。

1
2
3
4
5
6
7
8
def stamper=(object)
  object_stamper = if object.is_a?(ActiveRecord::Base)
    object.send("#{object.class.primary_key}".to_sym)
  else
    object
  end
  Thread.current["#{self.to_s.downcase}_#{self.object_id}_stamper"] = object_stamper
end

キーにはobject id(デフォルトではUserのobject id)使ってる。
controller側で毎回Thread.currentに操作者のidを保存して、それをmodelで取り出している。

んでobserverで同一の処理で呼ばれたのか判定する方法は。

同じようにThread.currentに設定してるキーと値を任意の値にすれば良さそう。
ActionControllerのbefore filterでキーはuserstampと同じようにobject idを使って、適当にkey _とかprefixつけてみる。
んで値はユニークな値を設定する。

1
2
3
def set_key
  Thread.current["key_#{User.object_id}"] = Time.now.strftime("%Y%m%d%H%M%S") #ミリ秒まで含めた方がいいと思われ。
end

observerでは

1
2
3
4
5
6
7
8
9
10
observe :user,:user_profile

def after_save(model)
  case model
  when User
    p Thread.current["key_#{User.object_id}"]
  when UserProfile
    p Thread.current["key_#{User.object_id}"]
  end
end

で同じ値が取得できた場合には同一の操作から呼ばれたものと考えられる。

この方法は副作用とかどうなんだろう。
そしてもっとスマートな方法はあるのだろうか・・・


jQuery Mobile 勝手に不正なページへ遷移してしまう時の対応

去年スマフォサイト作りました。

railsつかってます。
jQuery Mobileも使ってます。

mobileinitイベント内で

1
$.mobile.ajaxEnabled = false;

でAjaxオフにしてます。

でもたまにページロード完了後に、
勝手にローディング画像が出てきて、全然自分が見たいページとは違うページに飛ばされることがあります。

調べた結果Android端末の沢山とiPhone4以外(というか3GS?)で起きる現状でした。

上記現象が起こる操作は

Aページ- >[Bページへのリンクをクリック]- >Bページ- >[端末のブラウザバック]- >Aページ- >[Cページをクリック]- >Cページ読み込み完了後にBページに飛ばされる。

対策としては

1
$.mobile.pushStateEnabled = false;

でpushStateオフにしたら直りましたっていうお話。


Nヶ月前の月を求める

またまたRubyネタ。

Rails抜きでNヶ月前の月から今日までの月を求める必要があったので考えてみました。

すっごい遠回りして考えてるかもしれません。

rubyの配列はインデックスにマイナスを指定することができます。
その場合、参照する先は後ろから数えた値となります。

今日のコードはirbだけで試せます。
REPL最高。

1
2
3
a = [1,2,3]
a[0] # => 1
a[-1] # => 3

とりあえずNは3として、今月を含めて3ヶ月前を求めるには

1
2
month = [1,2,3,4,5,6,7,8,9,10,11,12]
month[Time.now.month-3]

1月ならば11月が帰ってきます。
2月ならば12月。3月なら1月となります。

インデックスは0始まりなのでそのあたりを考慮しないとあかんですね。

以上です。と書こうとしたけど、3ヶ月前から今日までの月でしたね。

1
3.times{ |i| p month[Time.now.month-i-1] }

先ほど述べたようにインデックスが0始まりなので-1してます。
配列で欲しいなら

1
3.times.map{ |i| p month[Time.now.month-i-1] }

かなぁ?