2010/04/19

fieldWithErrors div でレイアウトが崩れてしまう件

フォームで、activerecord error があると、対象の入力フィールドのレイアウトが崩れてしまう件。 問題は、エラーのあるフィールドが fieldWithErrors という div でくくられてしまうこと。 initializer を作るか、environment.rb に次を入れると、div が span に変わる
config/initializers/errors.rb
ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "<span class=\"fieldWithErrors\">#{html_tag}</span>" }
ついでに errors.css とか編集するといいかも
.fieldWithErrors {
  padding: 2px;
  background-color: red;
}
↓
.fieldWithErrors input, .fieldWithErrors select, .fieldWithErrors textarea {
  border: 2px solid red;
}

2010/04/15

各namespace でapplication_controller みたいな仕組みを作る

スカイプ英会話のカフェトークでは生徒、講師、管理者でそれぞれネームスペースをもってるんですが、各ネームスペースで application_controller のように、一つの親コントローラに共通メソッドをまとめたいと思いました。 まず、application_controller.rb に全てで使う共通メソッドなどを入れる。
class ApplicationController < ActionController::Base
  include SslRequirement              
  before_filter :set_user_language    
  before_filter :set_timezone

  def set_timezone
    Time.zone = session[:tz] if session[:tz]
  end
   
  private
  def set_user_language
    I18n.locale = params[:locale] || 'ja'
  end
   
end
次に、例えば管理者 admin ネームスペースで使うルートコントローラを作る (admin_root_controller.rb)
class AdminRootController < ApplicationController
  ssl_required :all
  before_filter :authenticate_admin
  before_filter :admin_login_required
  
  def admin_login_required
    if session[:admin_id]
      return true 
    else
      flash[:warning]= t('common.please_login')
      unless request.request_uri == "/admin/login/logout"
        session[:return_to_path] = request.request_uri
      end
      redirect_to :controller => "admin/login", :action => "index"
      return false
    end
  end

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      username == "xxxxx" && password == "xxxxxxxxxxxx"
    end
  end

  protected
  def ssl_required?
    Rails.env.production? #本番環境以外はSSL しない
  end

end
最後に普通のコントローラー
class Admin::LoginController < AdminRootController
  layout 'admin'
  skip_before_filter :admin_login_required

  def logout
    
  end

  def login

  end

end
こんな感じ・・・

2010/04/14

Filter chain halted as [:ensure_proper_protocol] でハマル

開発機でうまくいっていたページが本番機では Page Not Found エラーになってしまう。
ログを見てみると、なにやら Redirect しようとしている模様。
Redirected to http://cafetalk.com/en/user/reqs/confirm_req
Filter chain halted as [:ensure_proper_protocol] rendered_or_redirected.
なるほど、SslRequirement Pluginに、SSLが必要なページを渡してなかったのが問題でした。
ssl_requrement :index, :mycustom_action
404メッセージに惑わされて、ハマってしまいました orz.

2010/04/10

検索フォームにコダワル

いろんなところで使いそうなので、検索フォームの作り方をまとめてみた。(突っ込みどころは沢山あると思います。)

まず view
<% form_tag request.path, :method => 'get' do %>
<table>
 <tr>
  <td><%= t('common.created_at') %></td>
  <td><%= calendar_date_select_tag(:created_from, params[:created_from], :time => false)%> - <%= calendar_date_select_tag(:created_to, params[:created_to], :time => false)%></td>
 </tr>
 <tr>
  <td><%= t('common.keyword') %></td>
  <td><%= text_field_tag :keyword, params[:keyword], :size => 30 %> ID <%= text_field_tag :id, params[:id], :size => 10 %></td>
 </tr>
 <tr>
  <td>sort_by</td>
  <td><%= select_tag(:sort_by, options_for_select([['created_at', 'created_at'], ['updated_at', 'updated_at'], ['login_at', 'login_at'], ['ID', 'id'] ], 'created_at' ) ) %> <%=select_tag(:sort_dir, options_for_select([[t('common.asc'), 'ASC'], [t('common.desc'), 'DESC']], 'DESC'))%> 
  </td>
 </tr>
 <tr>
  <td></td>
  <td><%= submit_tag t('common.search_with_conditions') %> <input type="reset" value="Reset!"></td>
 </tr>
</table>
<%end %> 
リセットボタンは便利。また、select_tag にオプションを渡す場合は options_for_select を使用。
<%= select_tag(:sort_by, options_for_select([['created_at', 'created_at'], ['updated_at', 'updated_at'], ['login_at', 'login_at'], ['ID', 'id'] ], 'created_at' ) ) %>
selected = 'created_at' を指定している。

controller
    @users = User.full_search(params[:keyword], params[:page], :id => params[:id],
      :created_from => params[:created_from], :created_to => params[:created_to],
      :sort_by => params[:sort_by], :sort_dir => params[:sort_dir]
      )
model
メソッドには自由にパラメータを渡せるように options={} を使う
  def self.x_search(search, page, options = {})
    sql = "((display_name like '%#{search}%') OR (profile like '%#{search}%') OR (first_name like '%#{search}%') OR (last_name like '%#{search}%') OR (email like '%#{search}%') OR (mobile like '%#{search}%'))"

    if options[:id] && options[:id] != ""
      sql << " AND id = '#{options[:id]}'" 
    end

    if options[:created_from] && options[:created_from] != ""
      from = Time.parse(options[:created_from]).to_s(:db)
      # If timezone conversion required use Time.zone.parse(options[:created_to]).utc.to_s(:db)
      sql += " AND created_at >= '#{from}'"
    end
  
    if options[:created_to] && options[:created_to] != ""
      to = Time.parse(options[:created_to]).to_s(:db) 
      sql += " AND created_at <= '#{to}'"
    end
  

    if options[:sort_by] && options[:sort_by] != "" && options[:sort_dir] && options[:sort_dir] != ""
      order = options[:sort_by] + " " + options[:sort_dir]
    else
      order = 'created_at DESC'
    end    

    paginate(:per_page => 30, :page => page, :conditions => sql, :order => order)
  end

2010/04/06

Service Temporarily Unavailable でハマル

サイトをアクセスしようとすると、

Service Temporarily Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

と、503 エラー

/etc/httpd/logs/ で最新のエラーログを見ると
(111)Connection refused: proxy: HTTP: attempt to connect to 127.0.0.1:3000 (127.0.0.1) failed
ためしに、マニュアルで mongrel をリスタートしてみる
> mongrel_rails cluster::restart --clean
!!! Configuration file does not exist. Run mongrel_rails cluster::configure.
cluster::restart reported an error. Use mongrel_rails cluster::restart -h to get help.
Rails アプリケーションフォルダに入って、mongrel cluster configure してみる。
mongrel_rails cluster::configure -e production -p 3000 -N 2
mongrel_rails cluster::restart --clean
すると、スタートしたみたい。
** Daemonized, any open files are closed.  Look at tmp/pids/mongrel.3000.pid and log/mongrel.3000.log for info.
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with production environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready.  TERM => stop.  USR2 => restart.  INT => stop (no restart).
** Rails signals registered.  HUP => reload (without restart).  It might not work well.
** Mongrel 1.1.5 available at 0.0.0.0:3000
** Writing PID file to tmp/pids/mongrel.3000.pid

proxy: BALANCER: (balancer://mongrel_cluster). All workers are in error state
> ps aux

 
15:23   0:07 /usr/bin/ruby /usr/bin/mongrel_rails start -d -e production -p 3000 -P tmp/pids/mongrel.3000.pid -l log/mongrel.300
0.log
15:23   0:07 /usr/bin/ruby /usr/bin/mongrel_rails start -d -e production -p 3001 -P tmp/pids/mongrel.3001.pid -l log/mongrel.300
1.log

動いている。。。

2010/04/01

accepts_nested_attributes_for : 親モデルのフォームで子モデルも編集する方法

Service モデルが ServiceFile モデルと association で繋がっている場合。。。 ※ Railscasts 参照

models/service.rb

has_many :service_files, :dependent => :destroy
accepts_nested_attributes_for :service_files, 
    :reject_if => lambda { |a| a[:uploaded_data].blank? }, 
    :allow_destroy => true

models/service_files.rb

belongs_to :service

/service/new.html.erb & /service/edit.html.erb

<% form_for([:pro, @service], :url => {:action => :create},:html => {:multipart => true})  do |f| %>
  <!-- 子モデルにアップロードデータがあるので、multipart を指定 -->
  <% f.fields_for :service_files do |builder| %>
    <%= render "service_file_fields", :f => builder %>
  <% end %>
  <p><%= link_to_add_fields "Add", f, :service_files %></p>
<% end %>

_service_file_fields.html.erb

<div class="fields">
 <%= f.file_field :uploaded_data %>
 <%= link_to_remove_fields "<img src='/images/icons/cross.png'/>", f %>
</div>
※ application.js, application_helper.rb のメソッドは Railscasts 参照

はまったこと

undefined method `reflect_on_association' for NilClass:Class
・・・が
問題は<%= link_to_add_fields t('common.add_obj', :obj => t('file.singular')), f, :service_files %> に正しいクラスを渡してなかった。

<% form_for(@service, :url => {:action => :create},:html => {:multipart => true}) do |f| %>

を下に変えたら問題なし。(service は pro のネームスペース下)

< <% form_for([:pro, @service], :url => {:action => :create},:html => {:multipart => true}) do |f| %>