Hatena::ブログ(Diary)

130単位

2012-10-18

jQuery クエリパラメータを全リンクに付加

やりたいこと

コード

jQueryURL Parserというライブラリを利用します。

<script type='text/javascript' src='/javascripts/jquery.js'></script>
<script type='text/javascript' src='/javascripts/purl.js'></script>
$(function(){
  if (location.search == '') {
    return;
  }
  var req_params = $.url(location.href).param();
  $('a').each(function(){
    if (/^(#|https?:|\/\/)/.test($(this).attr('href'))) {
      return;
    }
    var parsed = $(this).url();
    var old_params = (parsed.attr('query') == '') ? {} : parsed.param();
    var new_path = parsed.attr('path') + '?' + $.param($.extend(old_params, req_params));
    $(this).attr('href', new_path);
  });
});

仕様

パラメータ配列が含まれていたり、リンクに同一パラメータが存在するかもしれないので、ハッシュ化してマージする仕様にした

留意点とか

  • GETメソッドのフォームにはactionにパラメータ追加しても効かない
    • hidden要素を追加する必要がある
  • Flashでのページ遷移があると効かない
  • そもそもJavaScriptオフだと効かない

※結局実運用せず見送った

2012-10-14

RSpecの書き方メモ #元住吉ハッカソン

Better Specsというサイトを題材にして、RSpecについてディスカッションしました。そのメモです。※訳ではないです

  • How to describe your methods
  • Test all possible cases
    • 正常系/異常系可能な限り網羅したほうがいいけど、程度にもよる
  • Use subject
    • 重複のあるexample groupと重複のないexample groupが1ファイルに混在する場合
      • 常にsubjectを使うように統一している
  • Use let and let!
    • 遅延評価されるletのほうがbeforeよりも良い
      • 複数のexampleで使われない変数もあるとき効率UP
    • beforeはあまり使わずなるべくletを使うようにしている
    • letで定義したものを別のlet内で利用してもok
    • letの中はなるべく1文で済ます
      • 複数の文が必要になる場合は素直にbeforeを使うべきでは
    • FixtureReplacementでオブジェクト生成に渡す引数をletでつくることもある
  • Mock or not to mock
    • ControllerのテストでModelをモック化すべきかどうかは一長一短
  • Use factories and not fixtures
    • なんとなく Fabrication > FactoryGirl の人が2人
  • 1つの example group で subject が複数できる場合はどうするか
    • そうならないように(テスト)設計する
    • 難しい場合はsubjectにこだわらず作業効率を優先
  • テストデータ
    • Database Cleanerなどでマスタデータ以外常にクリアした状態にする

まとめ

自分自身まだテスト書いたプロジェクト経験が少なく、書き方も定まっていないのですが、気になる点が話し合えて良かったです。

  • ユニットテスト(Modelなど)はletやsubjectを利用して書く
    • そう書けるようにシンプルにメソッドを実装する
  • 統合テスト(RequestSpec)は適切な粒度のexample groupで、その流れがテストされるのを優先して、subjectを使わずに書く
    • 複数の検証対象を"正常系"みたいな1つまとまりのitでやるのもありかなと

今後の方針としてはこんな感じでいこうかなと思いました。

関連リンク

RSpec自体の説明に良さ気な記事

元住吉ハッカソンギークハウス元住吉にて毎週やっておりますー

2012-10-02

CapistranoでローカルからApache設定の更新/反映

LPなどの関係でRewriteRuleをよく追加するようなアプリを運用していて、Apache設定ファイルもリポジトリに含めたいと思ったのがそもそものきっかけでした。それで一度タスクを作って運用していたのですが、いまいちな部分もあったので今回ブラッシュアップしてみました。

環境

旧レシピ

namespace :httpd do
  task :update_conf, :roles => :web do
    run "cp -rf /etc/httpd/conf.d ~/httpdconf_bak"
    sudo "cp -rf #{latest_release}/config/httpd/#{stage}/* /etc/httpd/conf.d/", :pty => true
  end

  after "httpd:update_conf", :roles => :web do
    sudo "/etc/init.d/httpd configtest", :pty => true
    sudo "/etc/init.d/httpd reload", :pty => true
  end
end
仕様
  • config/httpd 以下に設定ファイルを環境ごとに格納
  • アプリ自体をいったんデプロイ
  • cap httpd:update_conf 実行
    • 現在の設定ファイルをバックアップ
    • 最新リリースから設定ファイルをコピー
    • configtestしてreload(configtestでエラー発生したらreloadは実行されない)
課題
  • Apache設定反映のためにリポジトリにcommit/pushされている必要がある
    • ローカルから設定反映して成功したらリポジトリにcommitしたい
  • 環境ごとのディレクトリのため重複が発生している
    • 環境によって変わらない共通な設定は1箇所にまとめたい

で、これらの課題を解決したものをつくってみました。

新レシピ

namespace :httpd do
  namespace :deploy do
    set :local_src_path, "#{Dir.pwd}/config/httpd/conf.d"
    set :remote_src_path, "#{shared_path}/httpd/conf.d"
    set :remote_dest_path, '/etc/httpd/conf.d'

    task :default, :roles => :web do
      transaction do
        on_rollback do
          run "for file in `find #{remote_dest_path} -type l`; do sudo rm $file; done;", :pty => true
          run "rm -rf #{remote_src_path}"
          run "cp -rf #{remote_src_path}.prev #{remote_src_path}"
          sudo "ln -s #{remote_src_path}/*.{conf,passwd} #{remote_dest_path}", :pty => true
        end
        update
        reload
      end
    end

    task :setup, :roles => :web do
      run "mkdir -p #{shared_path}/httpd/conf.d #{shared_path}/httpd/conf.d.prev"
    end

    task :update, :roles => :web do
      #remove old symlinks
      run "for file in `find #{remote_dest_path} -type l`; do sudo rm $file; done;", :pty => true
      #backup old files
      run "rm -rf #{remote_src_path}.prev"
      run "cp -rf #{remote_src_path} #{remote_src_path}.prev"
      #upload new files
      Dir.glob("#{local_src_path}/{*,environments/#{stage}}.{conf,passwd}").each do |file|
        upload(file, remote_src_path, :via => :scp)
      end
      #create new symlinks
      sudo "ln -s #{remote_src_path}/*.{conf,passwd} #{remote_dest_path}", :pty => true
    end

    task :reload, :roles => :web do
      sudo '/etc/init.d/httpd configtest', :pty => true
      sudo '/etc/init.d/httpd reload', :pty => true
    end
  end
end
仕様
app_root/config/httpd/
└── conf.d
    ├── application.conf
    ├── environments
    │   ├── production.conf
    │   ├── production.passwd
    │   ├── staging.conf
    │   └── staging.passwd
    ├── passenger.conf
    └── ssl.conf

これで共通部分もDRYにできて、ローカルからの反映が可能になりました。

ファイルごとにシンボリックリンクを張るめんどくさい仕様なのは、Webサーバーデフォルト状態から極力離れないようにしたいという思いからです(他のアプリの設定ファイルをベタに置くことも考慮して)。シンプルにするなら、設定ファイルを入れるディレクトリシンボリックリンクにして Include deployed_conf/*.conf などとしたほうがいいと思います。

気になる点としては、もし異なるアプリを同じ仕組みで同じサーバーデプロイする場合は、アプリ名のプレフィックスなどをつける必要があるというところでしょうか。

わかったこと

Gist

もっとこうしたほうがいいとかあればご指摘いただけると助かります!

参考リンク