研究室配属システム構築ログ

2011/12/16 by 梅本隆史 = 配属システム作成 $ rails new haizoku_system $ cd haizoku_system $ nano Gemfile 以下を追記 gem 'therubyracer' gem 'bcrypt-ruby' # ログイン機能に必要 #配属システムに以下の機能をつける # 1, ユーザー登録 # 2, ユーザー認証 # 3, 希望研究室番号入力 # 4, 研究室登録 # 5, 集計結果一覧 # # 1と4はユーザーに使用できないようにする. == ユーザー登録機能作成 # user model は学籍番号とパスワードを格納する $ rails g scaffold user number:string password_digest:string パスワードを格納するフィールドの名称はpassword_digestとしなくてはならない点に注意 $ rake db:migrate $ nano app/models/user.rb 以下のように編集 class User < ActiveRecord::Base has_secure_password # 入力されたパスワードを設定・認証するメソッドと、確認パスワードを検証するバリデータと、認証機能が追加される. # 前に作成したpassword_digestフィールドは、裏でパスワードのハッシュ値を保存するのに使わる validates_presence_of :password, :on => :create # デフォルトではパスワード用の validates_presence_of 検証がないため、 # 新規ユーザが作成されたときに起動される検証機能を追加する必要がある end $ nano app/controllers/users_controller.rb 以下のように new, create メソッドを以下の編集 # coding : utf-8 ・・・・・ def new @user = User.new end def create @user = User.new(params[:user]) if @user.save render "new" end end $ nano app/views/users/new.html.erb 以下のように編集 <h1>ユーザー作成</h1> <%= form_for @user do |f| %> <% if @user.errors.any?%> <div class="error_messages"> <h2>Form is invalid</h2> <ul> <% for message in @user.errors.full_messages %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :学籍番号 %> <%= f.text_field :number %> </div> <div class="field"> <%= f.label :パスワード %> <%= f.password_field :password %> </div> <div class="field"> <%= f.label :パスワード確認 %> <%= f.password_field :password_confirmation %> </div> <div class="actions"><%= f.submit %></div> <% end %> == ログイン機能作成 === sessions コントローラ作成 $ rails g controller sessions $ nano app/controllers/sessions_controller.rb 以下のように編集 # coding : utf-8 class SessionsController < ApplicationController skip_before_filter :check_logined def new end def create user = User.find_by_number(params[:number]) if user && user.authenticate(params[:password]) session[:user_id] = user.id haizoku = Haizoku.find_by_user_id(user.id) redirect_to user_haizoku_path(user.id, haizoku.user_id), :notice => "ログインしました" else redirect_to login_path, :notice => "学籍番号とパスワードを確認してもう一度やり直してください" end end def destroy session[:user_id] = nil redirect_to login_path, :notice => "ログアウトしました" end end $ nano config/routes.rb 以下を追記 resources :sessions get "login" => "sessions#new", :as => "login" get "logout" => "sessions#destroy", :as => "logout" $ nano app/controllers/application_controller.rb 以下のように編集 class ApplicationController < ActionController::Base protect_from_forgery private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end helper_method :current_user # ヘルパーメソッドを定義して view から使えるようにした end $ nano app/models/user.rb 以下を追記 attr_accessible :number, :password, :password_confirmation # 編集可能なフィールドを指定 === ログインページの作成 $ nano app/views/sessions/new.html.erb <h1>研究室配属システム</h1> <%= form_tag sessions_path do %> <div class="field"> <%= label_tag :学籍番号 %> <%= text_field_tag :number, params[:number] %> </div> <div class="field"> <%= label_tag :パスワード %> <%= password_field_tag :password %> </div> <div class="actions"><%= submit_tag "ログイン" %></div> <% end %> ==希望研究室番号入力機能作成 # /users/user_id/haizoku/:id でユーザーが自分の希望研究室の番号を入力できるようにする # これまでの配属システムと違う点は,ユーザーを作った時点で user の haizoku も作成することが可能になった点である. # これによりユーザーに新規作成の作業をしてもらう必要がなくなった # ここでは以下のことをした. # 1, user モデルと haizoku モデルの関連付け # 2, user 作成時に haizoku も作る # 3, haizoku ページの編集 # 4, ログインしていない場合,ログインページにとぶ処理 # 5, user 自身の haizoku 以外のページを見れなくする処理 $ rails g scaffold haizoku first:integer second:integer third:integer fourth:integer fifth:integer user_id:integer $ rake db:migrate === user モデルと haizoku モデルの関連付け $ nano app/models/user.rb 以下を追記 has_one :haizoku $ nano app/models/haizoku.rb 以下を追記 attr_accessible :first, :second, :third, :fourth, :fifth :user_id# 編集可能なフィールドを指定 belongs_to :user $ nano config/routes.rb 以下のように編集 HaizokuSystem::Application.routes.draw do resources :sessions resources :users do resources :haizokus end end $ rake routes で haizokus が users にネストされたことを確認. user_haizokus GET /users/:user_id/haizokus(.:format) {:action=>"index",:controller=>"haizokus"} POST /users/:user_id/haizokus(.:format) {:action=>"create",:controller=>"haizokus"} new_user_haizoku GET /users/:user_id/haizokus/new(.:format){:action=>"new", :controller=>"haizokus"} edit_user_haizoku GET /users/:user_id/haizokus/:id/edit(.:format){:action=>"edit", :controller=>"haizokus"} user_haizoku GET /users/:user_id/haizokus/:id(.:format){:action=>"show", :controller=>"haizokus"} PUT /users/:user_id/haizokus/:id(.:format) {:action=>"update",:controller=>"haizokus"} DELETE /users/:user_id/haizokus/:id(.:format) {:action=>"destroy",:controller=>"haizokus"} === user 作成時に haizoku も作る $ nano app/controllers/users_controller.rb create メソッドを以下のように編集 def create @user = User.new(params[:user]) if @user.save @haizoku = @user.create_haizoku( :user_id => @user.id) render "new" else render "new" end end === haizoku に希望研究室の名前ものせるための処理 $ nano app/controllers/haizokus_controller.rb # 以下のように show メソッドを編集 def show @haizoku = Haizoku.find(params[:id]) # @first = Labo.find_by_number(@haizoku.first) # @second = Labo.find_by_number(@haizoku.second) # @third = Labo.find_by_number(@haizoku.third) # @fourth = Labo.find_by_number(@haizoku.fourth) # @fifth = Labo.find_by_number(@haizoku.fifth) respond_to do |format| format.html # show.html.erb format.json { render json: @haizoku } end end # @first などはユーザーが登録した研究室の情報を保持するために定義した. # あとでコメントアウトをはずす. === haizoku ページの編集 ログイン時に /users/:user_id/haizokus/:id/ にリダイレクトして 自分の研究室希望の状況を確認する画面に飛ぶようにしている. show.html.erb を編集する $ nano app/views/haizokus/show.html.erb 以下のように編集 <br> <%= @current_user.number %> さんの希望研究室の登録状況は以下のようになっています. <p> <b>第 1 希望:</b> <%= @haizoku.first %> <% if @first %><%= @first.name %> <% end %> </p> <p> <b>第 2 希望:</b> <%= @haizoku.second %> <% if @second %><%= @second.name %><% end %> </p> <p> <b>第 3 希望:</b> <%= @haizoku.third %> <% if @third %><%= @third.name %><% end %> </p> <p> <b>第 4 希望:</b> <%= @haizoku.fourth %> <% if @fourth %><%= @fourth.name %><% end %> </p> <p> <b>第 5 希望:</b> <%= @haizoku.fifth %> <% if @fifth %><%= @fifth.name %><% end %> </p> $ nano app/views/haizokus/_form.html.erb 以下のように編集 <%= form_for([current_user, @haizoku]) do |f| %># <= 編集見落とし注意 <% if @haizoku.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@haizoku.errors.count, "error") %> prohibited this haizoku from being saved:</h2> <ul> <% @haizoku.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :第1希望 %><br /> <%= f.number_field :first %> </div> <div class="field"> <%= f.label :第2希望 %><br /> <%= f.number_field :second %> </div> <div class="field"> <%= f.label :第3希望 %><br /> <%= f.number_field :third %> </div> <div class="field"> <%= f.label :第4希望 %><br /> <%= f.number_field :fourth %> </div> <div class="field"> <%= f.label :第5希望 %><br /> <%= f.number_field :fifth %> </div> <div class="actions"> <%= f.submit '更新',:disable_with => '処理中...'%> # ボタンの表示を"更新"にして,押したら,処理中の文字を出す </div> <% end %> $ nano app/views/haizokus/edit.html.erb 以下のように編集 <h1>編集画面</h1> <%= render 'form' %> ==haizoku のupdate メソッドの編集 以下のように編集する # coding : utf-8 ・・・・・ def update @haizoku = Haizoku.find(params[:id]) respond_to do |format| if @haizoku.update_attributes(params[:haizoku]) format.html { redirect_to [current_user, @haizoku], notice: '更新完了!' } format.json { head :ok } else format.html { render action: "edit" } format.json { render json: @haizoku.errors, status: :unprocessable_entity } end end end === ログインしていない場合,ログインページにとぶ処理 # コントローラーでメソッドを実行する前にログインしているか確認するフィルターをかける # すべてのコントローラーに適用したい場合は app/controllers/application_controller.rb に記述する # ただし,sessions コントローラーには適用させたくないので適用除外の処理をさせる $ nano app/controllers/application_controller.rb 以下のように編集 # coding : utf-8 class ApplicationController < ActionController::Base before_filter :check_logined protect_from_forgery private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end helper_method :current_user # 認証状況を確認するフィルタの定義 def check_logined if session[:user_id] begin @user = User.find(session[:user_id]) rescue ActiveRecord::RecordNotFound logger.error "セッションの情報(" + session[:user_id] + ")は user_infos には存在しません。" reset_session end end unless @user flash[:referer] = request.fullpath redirect_to login_path, :notice => "ログインしてください" end end end $ nano app/controllers/sessions_controller.rb 以下をメソッド定義の前に追記 # ログインページでは循環しないようにログインフィルタをスキップする skip_before_filter :check_logined === user 自身の haizoku 以外のページを見れなくする処理 $ nano app/controllers/haizokus_controller.rb 以下を追記 before_filter :user_checked # メソッド定義の前に記述 ・・・・・ private def user_checked unless params[:id] .to_i == current_user.id haizoku = Haizoku.find(current_user.id) redirect_to user_haizoku_path(current_user, haizoku.id) , :notice => "#{request.path}にはアクセスできません" end end == 集計結果を表示するページ(summary)の作成 $ rails g controller summary $ nano app/controllers/summary_controller.rb 以下のように編集 before_filter :check_logined def index @haizoku = Haizoku.find(current_user) # 表示させるために必要 @name = [] @head_count =[] @first = [] @second = [] @third = [] @fourth = [] @fifth = [] @labo = Labo.all i=0 @labo.each do |labo| @name[i] = labo.name @head_count[i] = labo.head_count @first[i] = Haizoku.count(:first, :conditions => ['first = ?',i+1]) @second[i] = Haizoku.count(:second, :conditions => ['second = ?',i+1]) @third[i] = Haizoku.count(:third, :conditions => ['third = ?',i+1]) @fourth[i] = Haizoku.count(:fourth, :conditions => ['fourth = ?',i+1]) @fifth[i] = Haizoku.count(:fifth, :conditions => ['fifth = ?',i+1]) i=i+1 end end $ nano app/views/summary/index.html.erb 以下のように編集 <h1>集計結果</h1> <table border=1> <tr> <td>研究室名</td> <td>第 1 希望</td> <td>第 2 希望</td> <td>第 3 希望</td> <td>第 4 希望</td> <td>第 5 希望</td> </tr> <% 0.upto(@labo.size-1) do |i|%> <tr> <td><%= @name[i] %>(<%= @head_count[i] %>)</td> <td> <%= @first[i] %> </td> <td> <%= @second[i] %> </td> <td> <%= @third[i] %> </td> <td> <%= @fourth[i] %> </td> <td> <%= @fifth[i] %> </td> </tr> <% end %> </table> $ nano config/routes.rb 以下を追記 get "summary"=> "summary#index", :as => "summary" # これで,/summary で集計ページにいくことができるようになった == 配属対象の研究室(labo)の登録ページ $ rails g scaffold labo name:string head_count:integer number:integer $ rake db:migrate # /labos に研究室一覧ページを作る $ nano app/controllers/labos_controller.rb # 以下のように index メソッドを編集 def index @haizoku = Haizoku.find(current_user) # ページを表示させるために必要 @labos = Labo.all respond_to do |format| format.html # index.html.erb format.json { render json: @labos } end end <h1>研究室一覧</h1> <table border=1> <tr> <th>研究室番号</th> <th>研究室名(受入人数)</th> </tr> $ nano app/views/labos/index.html.erb <% @labos.each do |labo| %> <tr> <td><%= labo.number %></td> <td><%= labo.name %>(<%= labo.head_count %>)</td> <td><%= link_to '見る', labo %></td> <td><%= link_to '編集', edit_labo_path(labo) %></td> <td><%= link_to '消去', labo, confirm: 'Are you sure?', method: :delete %></td> </tr> <% end %> </table> <br /> <%= link_to '研究室新規作成', new_labo_path %> # 実際に運用する時は必ず編集,消去,新規作成をコメントアウトする == ページの修正 # 見栄えを良くするための作業 # ほとんどRails によるアジャイルWebアプリケーション開発第4版からの引用 $ nano app/views/layouts/application.html.erb 以下のように編集 <!DOCTYPE html> <html> <head> <title>地球科学科研究室配属システム</title> <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body class="<%= controller.controller_name %>"> <!-- START_HIGHLIGHT --> <div id="banner"> <%= @page_title || "地球科学科研究室配属システム" %> </div> <div id="columns"> <% if @current_user %> <div id="side"> <ul> <li><%= link_to('ホーム', user_haizoku_path(current_user, @haizoku.id)) %></li> <li><%= link_to('編集画面', edit_user_haizoku_path(current_user, @haizoku.id)) %></li> <li><%= link_to('研究室一覧', labos_path) %></li> <li><%= link_to('集計ページ', summary_path) %></li> <li><%= link_to('ログアウト', logout_path) %></li> </ul> </div> <% end %> <div id="main"> <!-- END_HIGHLIGHT --> <% if @current_user %> <%= @current_user.number %> さん, 研究室配属システムへようこそ!!<br> <% end %> <% if notice %> <p id="notice"><%= notice %></p> <% end %> <%= yield %> <!-- START_HIGHLIGHT --> </div> </div> <!-- END_HIGHLIGHT --> $ nano app/assets/stylesheets/layout.css.scss 以下のように編集 # ただのコピペ #banner { background: #9c9; padding: 10px; border-bottom: 2px solid; font: small-caps 40px/40px "Times New Roman", serif; color: #282; text-align: center; img { float: left; } } #notice { color: #000 !important; border: 2px solid red; padding: 1em; margin-bottom: 2em; background-color: #f0f0f0; font: bold smaller sans-serif; } #columns { background: #141; #main { margin-left: 17em; padding: 1em; background: white; } #side { float: left; padding: 1em 2em; width: 13em; background: #141; ul { padding: 0; li { list-style: none; a { color: #bfb; font-size: small; } } } } } /* END_HIGHLIGHT */ == さらなる機能追加 # user が同じ研究室番号を入力できないようにする. # Ruby on Rails3 アプリケーションプログラミングp242-244 参照 $ nano app/model/compare_validator.rb 以下のように編集 # coding : utf-8 class CompareValidator < ActiveModel::EachValidator def validate_each( record, attribute, value ) cmp = record.attributes[options[ :compare_to ]].to_i case options[ :type ] when :not_equal record.errors.add( attribute, 'の番号はすでに入力されています.' ) if value == cmp # when :equal # record.errors.add( attribute, 'は同じじゃないです.' ) unless value == cmp else raise 'unknown type' end end end $ nano app/model/haizoku.rb # coding : utf-8 class Haizoku < ActiveRecord::Base @labos = Labo.all # 以下の :user_id はuser 作成後コメントアウトをする. attr_accessible :first, :second, :third, :fourth, :fifth, :user_id belongs_to :user # 以下のvalidates の後ろの :on => :update は create 時には適用しないことを意味する. # :on => :update をつけないと user 作成時に haizoku が生成されない. validates :first , :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => @labos.size}, :on => :update validates :second , :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => @labos.size}, :allow_blank => true, :on => :update validates :third , :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => @labos.size}, :allow_blank => true, :on => :update validates :fourth , :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => @labos.size}, :allow_blank => true, :on => :update validates :fifth , :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => @labos.size}, :allow_blank => true, :on => :update validates :second , :compare => { :compare_to => 'first', :type => :not_equal}, :on => :update validates :third , :compare => { :compare_to => 'first', :type => :not_equal}, :on => :update validates :third , :compare => { :compare_to => 'second', :type => :not_equal}, :on => :update validates :fourth , :compare => { :compare_to => 'first', :type => :not_equal}, :on => :update validates :fourth , :compare => { :compare_to => 'second', :type => :not_equal}, :on => :update validates :fourth , :compare => { :compare_to => 'third', :type => :not_equal}, :on => :update validates :fifth , :compare => { :compare_to => 'first', :type => :not_equal}, :on => :update validates :fifth , :compare => { :compare_to => 'second', :type => :not_equal}, :on => :update validates :fifth , :compare => { :compare_to => 'third', :type => :not_equal}, :on => :update validates :fifth , :compare => { :compare_to => 'fourth', :type => :not_equal}, :on => :update end
2012/03/04 堺 正太朗 作成