Railsでエラーページを表示する.
いつもpublic/404.htmlとか編集してる程度だったので、Railsの仕組みを使ったエラーページを作ろうかと色々試行錯誤してみた.
因にエラーページは通常はProductionモードでHTTPアクセスをローカルホスト( localhost / 127.0.0.1 )以外でアクセスしたときに確認できます.
エラーページの実装方法
方法はググってみると、ざっと3種類あった.
- public/404.htmlを書き換える
- ApplicationController内でrescue_fromを追記
- ApplicationController内でrescue_action_in_publicメソッドをオーバライド
色々やるなら、最後のrescue_action_in_publicオーバライドする方法が一番よさそう. ただ欲を言うとRails自身もProductionモードのときちゃんと、404.htmlとか表示してるからその機能っぽくしたい.
Railsのコードを読んでみた.
ActionController::RescueにDEFAULT_RESCUE_RESPONSESなんてモノがあるじゃないか!コレを使ってRailsは例外からHTTPステータスコードに変換して404.htmlを表示してた.
DEFAULT_RESCUE_RESPONSES = { 'ActionController::RoutingError' => :not_found, 'ActionController::UnknownAction' => :not_found, 'ActiveRecord::RecordNotFound' => :not_found, 'ActiveRecord::StaleObjectError' => :conflict, 'ActiveRecord::RecordInvalid' => :unprocessable_entity, 'ActiveRecord::RecordNotSaved' => :unprocessable_entity, 'ActionController::MethodNotAllowed' => :method_not_allowed, 'ActionController::NotImplemented' => :not_implemented, 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity }
ステータスコード自体はActionController::StatusCodes::STATUS_CODESで管理してた.
STATUS_CODES = { : : 200 => "OK", : 404 => "Not Found", : 500 => "Internal Server Error", : : }
例外毎にHTTPステータスを割り当てて表示する方法.
Railsのコードを読んで大体なにしてるか分かったので、404.htmlの用に例外からステータスコードを判定して表示するコードを書いてみた.
renderでレイアウトとか色々してるけどこの辺は普通のコントローラと書き方は同じ.
class ApplicationController < ActionController::Base # 例外とステータスの関連付け rescue_responses.update({ 'Foo::BarError' => :not_found }) protected # 確認をhttp://localhost 等で確認するときはコメントアウト # が定石 #def local_request? # false #end def rescue_action_in_public(exception) #:doc: status_message_sym = response_code_for_rescue(exception) || :internal_server_error @http_status_code = SYMBOL_TO_STATUS_CODE[status_message_sym] respond_to do |format| format.html { render :template => "commons/error.html", :layout => 'application', :status => @http_status_code } end end end
テンプレートをRAILS_ROOT/views/commons/error.html.erbに作成して、Productionモードで確認.
<% title = "#{@http_status_code} #{ActionController::StatusCodes::STATUS_CODES[@http_status_code]}" -%> <h1><%= title %></h1> <p> <% if @http_status_code == 403 # Forbidden -%> アクセス権限がありません. <% elsif @http_status_code == 404 # Not Found -%> お探しのページが見つかりませんでした. <% elsif @http_status_code == 422 # Unprocessable Entity -%> 指定されたエンティティの処理ができませんでした. <% elsif @http_status_code == 500 # Internal Server Error -%> システムに障害が発生しました. <% end -%> </p>
[Java]JTableの選択状態をトグルにする
普通、テーブルの行選択にCtrlを押しながらクリックすると選択をトグル式に選択したり選択を外したりできる.
普段PCのUIに慣れている人は当たり前だけどCtrlを押しながらクリックは知らない人には使いにくい(っという人がいる)
個人的にはそれくらい覚えといて良いだろうと思うけど...
そこでJTableでそのような挙動をさせるためにはchangeSelectionしてトグルを常にtrueにすればできたー.
new JTable(){ @Override public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) { super.changeSelection(rowIndex, columnIndex, true, extend); } }
これは以下のクラスのメソッドからコールされていました.
javax.swing.plaf.basic.BasicTableUI$Handler.mousePressed
[Git][Mac] MacでGitのサブコマンドのタブ補完をする方法
MacPortsでgitとbash-completionをインストール
$ sudo port install git-core $ sudo port install bash-completion
.bashrcを修正
if [ -f /opt/local/etc/bash_completion ]; then . /opt/local/etc/bash_completion fi if [ -f /opt/local/share/doc/git-core/contrib/completion/git-completion.bash ]; then . /opt/local/share/doc/git-core/contrib/completion/git-completion.bash fi
これでサブコマンドが補完されると思います.
ARで論理削除する為のモジュールを作ってみた.
Railsで論理削除が必要になったのでとりあえず調べてみた.希望としては
- プラグインに頼らず手軽に使いたい
- ARの機能そのままで使いたい
調べてみると以下の二つがでてきた.
acts_as_paranoidがググってみると結構多いが別に高機能を求めてる訳じゃないのでこれは使いたくない、っでRails作るときに参考にしているskipのソースを読むとモジュールにしているのがあった.
こっちは手軽で便利だけど論理削除を実装で意識する必要が強いのでこれを参考にオレオレモジュールを作ってみた.
論理削除オレオレモジュール
module Models # # 論理削除するようにします. # # 論理削除にはdeleted_atカラムを使用します.マイグレーションにdatetime型のカラムを追加してください. # 削除の判定にはdeleted_atがnullでない場合は削除済みとして扱われます. # # # find等のメソッドから削除されたレコードを取得しないようにする例 # class Member < ActiveRecord::Base # include Models::LogicalDestroyable # default_scope :conditions => {:deleted_at => nil} # end # # # default_scopeを指定する場合 # class Member < ActiveRecord::Base # include Models::LogicalDestroyable # hide_destroyed_with_default_scope # end # module LogicalDestroyable def self.included(base) base.alias_method_chain :destroy, :logical_destroyable base.named_scope(:active, {:conditions => {:deleted_at => nil}}) base.attr_protected :deleted_at end # # default_scopeを使って削除されたレコードを検索結果から隠します # # findメソッドなどで削除項目も対象にしたい場合はwith_exclusive_scopeを使ってください. # def self.hide_destroyed_with_default_scope # create時にも初期値として使われるけど大丈夫かな? self.default_scope :conditions => {:deleted_at => nil} #default_scopeは最後のdefault_scopingの値しか見ないので使えないっぽいからdefault_scopeを使うときは都度指定する必要がある. #options = {:conditions => {:deleted_at => nil}} #base.default_scoping.insert 0, { :find => options, :create => options[:conditions] } end def destroy_with_logical_destroyable before_destroy_with_logical_destroyable current_time = current_time_from_proper_timezone ret=update_attribute(:deleted_at, current_time) after_destroy_with_logical_destroyable ret end def before_destroy_with_logical_destroyable;end def after_destroy_with_logical_destroyable;end def destroyed? !self.deleted_at.nil? end alias_method :deleted?, :destroyed? def recover update_attribute(:deleted_at, nil) end end end
あとがき
とりあえずモジュール作ってみたけどdeleteとdestroyって似てるけど内部の処理が違うんだよなー.
- delete
- DELETE文(SQL)で消す
- destroy
- ARオブジェクトを一旦作ってから削除
- before_destroyコールバックで関連付けされてるやつも消したりするように成ってる.
関連づけ先が勝手に消されないようにbefore_destroyコールバックとは別のコールバックするようにしたけど.
deleteをオーバライドして関連まで影響ない仕様をそのまま論理削除に置き換えて作れば良かったかも.
物理削除の場合はdestroyするようにすればARの仕様を大きく変えないしなー.
Rails2.3でerror_message_onのi18n対応
なぜかRails2.3のerror_message_onはi18n対応されてないのでしてみました.
対応前 before
error_message_on(:user, :name) => "を入力してください。"
対応後 after
error_message_on(:user, :name) => "名前を入力してください。"
コード
プラグインなり、初期化時なり適当な(rails起動時に一度だけ実行する)ところで読み込ませてください.
module ActionView module Helpers module ActiveRecordHelper # エラーメッセージの翻訳 alias_method :error_message_on_without_translate, :error_message_on def error_message_on(object, method, *args ) obj = instance_variable_get("@#{object}") options = args.extract_options! options.reverse_merge!(:prepend_text => obj.class.human_attribute_name(method.to_s)) if args.empty? args = options else args.push options end error_message_on_without_translate(object, method, args) end end end end
jpmobileでsession_keyが空のときがある気がする.
自分の環境だけかもしれないけど、request.session_options[:key]がnilの時があったのでとりあえず、対応してみた.
元の実装よりも判定を増やした程度なのでこのままでもいい気がするけど様子見.
Index: trans_sid.rb =================================================================== --- trans_sid.rb (リビジョン old) +++ trans_sid.rb (リビジョン new) @@ -87,7 +87,9 @@ private # session_keyを返す。 def session_key - (request.session_options || ActionController::Base.session_options)[:key] + opts1 = request.session_options + opts2 = ActionController::Base.session_options + (opts1 && opts1[:key]) || (opts2 && opts2[:key]) end # session_idを返す def jpmobile_session_id
RailsでプロジェクトのSVNバージョンを表示する
今作ってるシステムでSVNのバージョンを表示させようと思いって書いてみました.
config/environment.rbを編集
config/environment.rbに以下を追加
APP_CONFIG = {} APP_CONFIG[:admin_email] = "MyApp <admin@example.com>" APP_CONFIG[:site_name] = "MyApp" APP_CONFIG[:major_version] = "0" APP_CONFIG[:minor_version] = "1" APP_CONFIG[:revision] = nil # バージョンを拾ってこれれば拾ってくる. begin require "rexml/document" src = `svn info --xml #{RAILS_ROOT}` unless src.nil? || src.empty? doc = REXML::Document.new src APP_CONFIG[:revision] = doc.elements['/info/entry'].attributes['revision'] end rescue end APP_CONFIG[:version] = "#{APP_CONFIG[:major_version]}.#{APP_CONFIG[:minor_version]}" APP_CONFIG[:version] += ".#{APP_CONFIG[:revision]}" unless APP_CONFIG[:revision].nil? APP_CONFIG[:full_version] = "#{APP_CONFIG[:site_name]} ver.#{APP_CONFIG[:version]}"
viewを編集
viewのバージョンを表示したい場所に以下を追加
<%= APP_CONFIG[:full_version] -%>