ラベル rails の投稿を表示しています。 すべての投稿を表示
ラベル rails の投稿を表示しています。 すべての投稿を表示

2013年2月14日木曜日

railsでDBのトリガーっぽいことをやる

やりたい事は以下の
『モデルAを作成(DBにレコードを登録)した際に、別のモデルも登録する』
みたいなの。

抽象的に書くと分からんので具体例にすると『全種類合わせた商品購入履歴を登録する』みたいなやつ。
例としては、ぶき・ぼうぐ・どうぐの購入履歴テーブルがあった時に、全部の購入履歴をと管理するようなテーブルを同時に用意しておくみたいなやつ。

こんなもん『SQL結合すれば専用テーブル作らんでもいいだろ』というはなしもあるのですが、以下のメリットがあるので作ることにしました。

  • 複雑なSQLを書かなくてて済む
  • 履歴⇒履歴詳細データという辿り方が簡単にできる(前購入履歴には各履歴への参照キーを持たせる想定)


よくある話なのでWeb上にもそこら中に情報が載ってますが、備忘録として書いておきます。
#railsはモデルの関連性が簡単にかけるのでこの手の話はすぐできる。
#あと、そもそももっと上手くやる方法があるのかもしれん風来のシレン。

整理すると

  • やりたいこと:あるレコード新規登録時に別のテーブルにもレコードを登録するようにする
  • 実現の基本方針:(きっと)railsのActiveRecordにはいい感じのメソッドがあるのでそれでやる


で、以下のようにした。
# 武器購入履歴モデル
class BukiKounyuRireki < ActiveRecord::Base
  # 以下の情報を持つ
  # 購入した武器のID
  # 購入者の名前
  # 個数
  attr_accessible :buki_id, :user_id, :count

  # レコード登録時に発動するコールバックafter_createメソッドを定義
  # |record| は登録した元レコードの中身が入っている
  after_create {|record|
    # 前購入履歴テーブルにレコード(購入者、武器購入履歴ID[武器IDではない],購入物タイプ)を登録する
    AllRireki.create user_id: record.userid, rireki_primary_id: record.id, item_type: 1
  }
end

サマリーすると、

  • after_createメソッドを使って定義します
  • フック元のレコードはafter_create のブロック内で宣言した第一引数として使用できます(正確な説明ではない可能性が高い…)

ということです。

↑のモデル関連だとあんまり力を発揮しませんが、履歴の幅を広げて、
どんな道具を使ったか記録したい
誰と話したか記録したい
とか言うように、元履歴のテーブルの構造がバラバラになれば今回のようなモデル(全履歴管理モデル)がパワーを発揮してくる(とおもう)。

2013年2月9日土曜日

deviseのヘルパーメソッド(user_signed_in?,current_user,user_session)について調べてみたその2 ~実行してみる編~


前回に引き続きdeviseのヘルパーメソッド(user_signed_in? ,current_user,user_session)について実際に実行して調べてみます。

ちなみに読むのがめんどい人は以下の結論


user_signed_in?:
操作してる人がログインしてればtrue、してなきゃfalse。
他の人のログイン状態とかは関係ない。

current_user:
操作している人のユーザ情報(deviseの認証につかってるやつ)がとれる。
ログインしてなきゃnil。
ムロン他の人のは取れない。

user_session:
初期状態は空っぽ。
(おそらく)ユーザごとのセッションデータのいれもの。


だけ目を通してマンガ喫茶に行ってジョジョの奇妙な冒険でも読むのがいいと思います。

以下のようなコードを書いておき
    logger.debug 'ここからテスト用ログ'
    result = user_signed_in?
    logger.debug 'result is ' + result.to_s

    login_user = current_user
    if login_user
      logger.debug 'current user email is ' + login_user.email.to_s
    else
      logger.debug 'current user is nil'
    end

    # 現在のログインユーザ情報を取得
    login_user = user_session

    # 一応nilチェック
    if login_user
      logger.debug 'login_user :' + login_user.to_s
    else
      logger.debug 'login_user is nil'
    end
    logger.debug 'テスト用ログここまで'



以下のようなパターンで出力させてみる。
  1. 誰もログインしていない場合
  2. test1ユーザだけがログインしている場合でそのセッションを張っているブラウザからアクセス
  3. test1ユーザだけがログインしている場合でそのセッションを張っていないブラウザからアクセス
  4. test1,test2ユーザ二人がログインしている場合に各々のセッションを張っているブラウザからアクセス
このうち3,4については同じ種類のブラウザだと、
『セッションが共有されて上手くテストができない!』*1
とか言うケースも考えられるのでchromeとFireFox2つのブラウザから試して見ることにした。
で、結果は以下。
1. 誰もログインしていない場合
ここからテスト用ログ
result is false
current user is nil
login_user is nil
テスト用ログここまで

2.test1ユーザだけがログインしている場合でそのセッションを張っているブラウザからアクセス
ここからテスト用ログ
  User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
result is true
current user email is test1@aa.bb
login_user :{}
テスト用ログここまで

3.test1ユーザだけがログインしている場合でそのセッションを張っていないブラウザからアクセス
ここからテスト用ログ
result is false
current user is nil
login_user is nil
テスト用ログここまで

4. test1,test2ユーザ二人がログインしている場合に各々のセッションを張っているブラウザからアクセス
From test1
ここからテスト用ログ
  User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
result is true
current user email is test1@aa.bb
login_user :{}
テスト用ログここまで

From test2
ここからテスト用ログ
  User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
result is true
current user email is test2@aa.bb
login_user :{}
テスト用ログここまで

という結果になった。
まとめると。
user_signed_in?:
リクエストを発行したブラウザがログインしていない場合はfalse(他に誰がログインしていようが関係ない)
リクエストを発行したブラウザがログインしている場合はtrue

current_user:
リクエストを発行したブラウザがログインしていない(ログイン済セッションを張っていない)場合はnilが返る。
リクエストを発行したブラウザがログインしている(ログイン済セッションを張っていない)場合は、『発行ブラウザでログインしたユーザの情報のみ』が取得できる。

user_session:
常に空っぽ。
まあこれはなんにも詰めていないからだと推測できる。

ということになり、いわゆる基本的なログイン情報の取得(既にログイン済なのか、ユーザ情報はなんだっけ)に使えそうなメソッドであることがわかった。
よかったよかった。


1:          ,. -‐'''''""¨¨¨ヽ
         (.___,,,... -ァァフ|          あ…ありのまま 今 起こった事を話すぜ!
          |i i|    }! }} //|
         |l、{   j} /,,ィ//|       『おれは新しいウィンドウでログインしようと
        i|:!ヾ、_ノ/ u {:}//ヘ        思ったらいつのまにかログインしていた』
        |リ u' }  ,ノ _,!V,ハ |
       /´fト、_{ル{,ィ'eラ , タ人        ど… どういう機構なのか わからねーと思うが
     /'   ヾ|宀| {´,)⌒`/ |<ヽトiゝ        おれも何をされたのかわからなかった…
    ,゙  / )ヽ iLレ  u' | | ヾlトハ〉
     |/_/  ハ !ニ⊇ '/:}  V:::::ヽ        セッション管理がどうにかなりそうだった…
    // 二二二7'T'' /u' __ /:::::::/`ヽ
   /'´r -―一ァ‐゙T´ '"´ /::::/-‐  \    キャッシュだとかリバースプロキシだとか
   / //   广¨´  /'   /:::::/´ ̄`ヽ ⌒ヽ    そんなチャチなもんじゃあ 断じてねえ
  ノ ' /  ノ:::::`ー-、___/::::://       ヽ  }
_/`丶 /:::::::::::::::::::::::::: ̄`ー-{:::...       イ  もっと恐ろしいものの片鱗を味わったぜ…

2013年2月8日金曜日

deviseのヘルパーメソッド(user_signed_in?,current_user,user_session)について調べてみたその1 ~API読む編~

今回はdeviseのヘルパーメソッドメソッドについて調べてみます。

事の発端は
はて、devise使ってる時にログインしているユーザのIDとか名前とかセッションに入ってるんだっけ?
⇒入ってるならどうやって取るんだっけ?
⇒なんかそういうヘルパーメソッドあるんじゃなかったっけ?
⇒そもそもそのヘルパーメソッドって何なんだっけ?
というあたりでした。

というわけでdeviseのヘルパーメソッド
  • user_signed_in?
  • current_user
  • user_session
について見てみることにします。

知っておきたいのは以下のあたり。
  • 仕様(何を入れたら何が返るか)
  • どこにいるのか(どのクラスにいるのか)
  • どう使えばいいか
というわけでサーフィンしに行ったですが、探し方が悪いのかあんまりいい感じの結果は出てきませんでした><
せいぜい以下のカンジ。
  • 3つのヘルパーメソッドがあります
  • current_userを使うと現在のユーザ情報が取れます
  • ログインチェックにはuser_signed_in?を使います。
となればオフィシャル(?)ドキュメントを見るしかなかろう、ということで
を見に行きました。

フレーム左部分の

Classes | Methods | Files 
からMethodを選択して、『user_signed_in?』とかで探してみるとなんもない。
うむ、自動生成されてるからプレフィックスがいかんのかもしれんというわけで『signed_in』とかで探してみると…

あったあった、よかったよかった。

で内容は

- (Booleansigned_in?(scope = nil)

Return true if the given scope is signed in session. If no scope given, return true if any scope is signed in. Does not run authentication hooks.
Returns:
  • (Boolean)
とかなので、(いんちき)翻訳すると。

入力:scope(なくてもいい)
出力:
引数で渡されたスコープがログイン済である(is signed in session) or スコープが渡されていない場合は任意のスコープでログインしている ⇒ true
false:それ以外
説明:本メソッドは認証フックを発生させない。

うーん、大体ニュアンスは分かるのだが、スコープという単語の意味がわからないのでメソッドの↑の方を探してみたところ。
Define authentication filters and accessor helpers based on mappings. These filters should be used inside the controllers as before_filters, so you can control the scope of the user who should be signed in to access that specific controller/action. Example:
Roles:
  User
  Admin

Generated methods:
  authenticate_user!  # Signs user in or redirect
  authenticate_admin! # Signs admin in or redirect
  user_signed_in?     # Checks whether there is a user signed in or not
  admin_signed_in?    # Checks whether there is an admin signed in or not
  current_user        # Current signed in user
  current_admin       # Current signed in admin
  user_session        # Session data available only to the user scope
  admin_session       # Session data available only to the admin scope

Use:
  before_filter :authenticate_user!  # Tell devise to use :user map
  before_filter :authenticate_admin! # Tell devise to use :admin map
とか書いてあるので、userスコープ(多分一般ユーザ)とadminスコープ(多分管理者ユーザ)の事だと思う。

つまり使い方としては、userスコープかadminスコープかもしくは何も指定しないで実行すると、該当のスコープに所属するユーザのセッションが張られている(ログインしている)かどうかを判別できる、というカンジだと思う。

なーる。
では current_user は? と思ってさっきのフォームから探してみたのだが無い。
と思ったけど、↑のやつにcurrent_user っているぞ? どうなってんだ?
で、チョットそこを読んでいくと下の方に
というリンクが。
展開してみると。
# File 'lib/devise/controllers/helpers.rb', line 42

def self.define_helpers(mapping) #:nodoc:
  mapping = mapping.name

  class_eval <<-methods data-blogger-escaped-1="" data-blogger-escaped-:="" data-blogger-escaped-__file__="" data-blogger-escaped-__line__="" data-blogger-escaped-authenticate_="" data-blogger-escaped-current_="" data-blogger-escaped-def="" data-blogger-escaped-devise_controller="" data-blogger-escaped-end="" data-blogger-escaped-force="" data-blogger-escaped-if="" data-blogger-escaped-mapping="" data-blogger-escaped-opts.delete="" data-blogger-escaped-opts="" data-blogger-escaped-scope="" data-blogger-escaped-signed_in="" data-blogger-escaped-warden.authenticate=""> :#{mapping})
    end

    def #{mapping}_session
      current_#{mapping} && warden.session(:#{mapping})
    end
  METHODS

  ActiveSupport.on_load(:action_controller) do
    helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session"
  endend


と出てきた。
define_helpers とあるのでそのものズバリメソッドを自動生成(生成という単語は適切では無い気がするが)するメソッドなのだろう。
で見るとたしかに

#{mapping}_signed_in?
current_#{mapping}
#{mapping}_session

という3つのメソッドがあるため、探したかった
  • user_signed_in?
  • current_user
  • user_session
が自動生成されるのだと読み取れる。
が、これ以降のコードの読み解きは現段階ではムズカシイので一旦パスすることにする。
#なんか↑のコード @current_#{mapping} ||= warden.authenticate(:scope => :#{mapping})
だと、ログインしているユーザ全部とれちゃいそうな気がするのだが。。。

というわけで次回は百聞は1実行に如かずということで実際に打ってためしてみよう。
一旦以上。

2013年2月6日水曜日

deviseの標準画面をカスタマイズする ~標準画面をカスタマイズする その2~

今回は前回作ったこれ


をこう


しようという話です。
#チョットきれいになります。

なんかはじめにやりたかったことは以下のあたりです。

  • 見出し(ログインという文字)のフォントをメイリオ(最近気に入ってます)にしよう
  • 見出しは中央寄せがいいなぁ
  • 見出しとフォームの間に仕切り線が欲しいなあ
  • ログインのフォームはは中央寄せがいいなあ
  • ボタンはカッコいいのがいいなぁ

で、結果的に↑のようになりましたので、その流れを見ていきます。

まず見出しものについてですが、これはまあそんなに難しくはなく以下のカンジだろうと思ってました。

  1. とりあえず見出し部分はdivで囲もう
  2. divの中身をtext-align:center;とかすればいいだろう
  3. h2のフォントでも指定すればいいだろう
  4. 見出しの下にhrでもつければいいだろう

というわけでソース(html.erb)を以下のように修正。
#といってもdivで囲ってその下にhrつけただけ。

ログイン


これに対して以下のCSS(railsなのでscss記法です)を当てると見出し部分ができます。
  // 画面全体の背景色
  background-color: #f5f5dc;

  // タイトル部分のスタイル指定
  .page-title {
    text-align: center;
    h2 {
      font-family: 'メイリオ',Meiryo,'MS Pゴシック',sans-serif;
    }
  }

次にログインフォームの部分。
これも以下のカンジで1つ1つ解決しました。

  1. 角丸・白抜きにするためにフォーム全体をdivで囲ってwidthを指定しよう(widthを指定しないと角丸が画面全体になってしまう)
  2. 角丸はborder(仕切り線)とborder-radiusでやればいいや
  3. ラベルとテキストボックスとボタンはpadding-leftでいい感じの位置になるようにしよう
  4. bootstrapにはかっこいいボタンのクラスが用意されていたからそれを当てよう

というのは虚偽報告で、実際はchromeのdeveloper tool*1使いながらガチャガチャCSS当てて行ってなんとか今の形にしました。
#レイアウティングやCSS当てが得意な人ならいきなり最終型にいけるんだろうが、ワタクシにはそのような力はありません。

というわけでソースを以下のように修正。
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<%= f.label 'メールアドレス' %> <%= f.email_field :email, :autofocus => true %>

<%= f.label 'パスワード' %> <%= f.password_field :password %>

<%= f.submit "ログイン", {class: 'btn btn-primary'} %>
<% end %>

ボタンをかっこ良くするにはsubmitボタンのclassに 'btn btn-primary' を指定することで実現しています。
#bootstrapにはボタンをかっこ良くするためのCSSクラス定義が既に準備されている。

これに対して以下のCSSを当てるとログインフォーム部分ができます。
  // ページコンテンツ(ここではログインフォームのみ)のスタイル指定
  .page-contents {
    // ログインフォームのスタイル指定
    #div-login-form {
      // サイズ系指定
      width:280px;
      margin:10px auto;
      padding-top: 10px;

      // フォーム全体の背景色(白抜き部分)
      background-color: window;

      // ------フォームを囲っている角丸の部分ここから------
      // 角丸の線スタイル(太/色/線種)指定
      border: 5px solid #002a80;

      // 角丸表現(クロスブラウザ対応のため一応3パターン指定)
      border-radius: 5px;
      -webkit-border-radius: 5px;
      -moz-border-radius: 5px;
      // ------フォームを囲っている角丸の部分ここまで------

      // フォーム内のコミットボタン以外は左端から30pxとする
      div {
        padding-left: 30px;
      }

      // フォーム内のコミットボタンだけは左端から100pxとする
      // #class指定がform種別指定より優先度が高いので有効になる
      .div-commit-button {
        padding-left: 100px;
      }
    }

そこそこきれいな画面ができたので今回のdeviseの標準画面をきれいにするシリーズは一旦以上とします。
#そこそこ綺麗な画面ができたので次回は別の話をしようかと。

1:現在のドキュメントの構造を確認したりCSSの適用状態を確認したりJavaScriptのデバッグを確認したりできる、とんでもなくすばらしいツール。

2013年2月5日火曜日

deviseの標準画面をカスタマイズする ~標準画面をカスタマイズする~

というわけで今回はdeviseの画面をカスタマイズします。
カスタマイズ手順にありがちな以下の感じでやっていきます。
  1. 現在の画面の仕様を理解する(知らずににいじると不完全な機能をもった画面になるので)
  2. 現在の画面をバックアップする(この辺はgitでも使ってりゃいらないんだろうけど、とりあえず今回はやっておきます)
  3. どういうカスタマイズをするか決める
  4. カスタマイズする

1. 現在の画面の仕様を理解する。
ざっくり理解(というかそんなに難しくない)します。
前にやってきた標準画面の整理の話とソースをみると以下の点がわかる。
画面遷移については以下
  • ログインフィルターでNGが出るとこの画面に飛んでくる
  • ここからログイン処理を呼び出してOKだと標準画面(root_to で指定する所)に飛ぶ
  • この画面からユーザ登録画面(SignUp)に飛べる
  • この画面からパスワードリセットメール発行画面に飛べる
画面の項目については以下
ログイン用フォーム
  • email入力フォーム
  • password入力フォーム
  • remember_meチェックボックス
リンク
  • ユーザ新規登録へ
  • パスワードリセットメール発行へ
ポイントとしては以下2点だと考えます。

  • フォームの飛び先(ひじょうにきほんてきですが)
  • リンクを出力しているコード

フォームの飛び先と何をPOSTするかを理解していれば自動生成されたViewを使わずとも、自分でログイン用フォームを作れるでしょう。

あと、リンクを出力する部分はerb中で
<%= render "devise/shared/links" %>
のように部分テンプレートで描画している。
⇒ つまり片方のリンクだけ消すとかいうのは(さしあたり)ムリなように見える。

ちなみに該当の部分テンプレートの中身は以下のカンジ。
#毎度のごとく日本語コメントはコチラで追加。

<%- if controller_name != 'sessions' %>
  
  <%= link_to "Sign in", new_session_path(resource_name) %>

<% end -%>



<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
  
  <%= link_to "Sign up", new_registration_path(resource_name) %>

<% end -%>



<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
  
  <%= link_to "Forgot your password?", new_password_path(resource_name) %>

<% end -%>


<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
  <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>

<% end -%>




<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
  
  <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>

<% end -%>



<%- if devise_mapping.omniauthable? %>
  
  <%- resource_class.omniauth_providers.each do |provider| %>
    <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>

  <% end -%>
<% end -%>


のように『現在のユーザの状態を判断して必要そうなリンクを出力する』のようになっているので、標準以外で行くならココの部分の必要なリンクだけ抜き出すのがいいでしょう。

というわけでざっくり以下の理解と課題抽出ができました。

  • ログインフォーム(formの部分ね)のPOST先と中身
  • リンクを出力する機構
  • confirmable,lockable,omniauthableが示す機構については調べないと


2. 現在の画面をバックアップする
ホントgitを使えばこんなのいらなくて、こんなことをやっているのがバレたら、
『アムロ! バージョン管理ツールを使わずにファイルの手動バックアップを繰り返す人類など、システム開発のノミだということがなぜ分からん!』
とか罵られて
アクシズを落とされても文句が言えないレベル
だと思うのですが、さしあたりできてないのでカッコ悪いバックアップします。
ログイン画面を表示しているのは
{railsアプリルート}/views/devise/sessions/new.html.erb
なので、同じ場所に new.html.erb.bcup という名前でバックアップしておきます。
#ださい。。。

3. どういうカスタマイズをするか決める
難しいカスタマイズは後回しにしてまずは簡単なところから行きましょう。
というわけで以下のカンジ。

  • 表示内容を日本語化する
  • rememberチェックボックスはよくわからないから一旦外す
  • ユーザ登録とパスワードリセットのリンクも一旦外す(遷移を封印して管理対象を少なくするのがねらい)


4. カスタマイズする
↑のカスタマイズをしていきます。
まずは簡単なリンクの封印から。
これは↑で調べておいた通りリンクを描画する部分テンプレートを封印すればいい。

次に表示内容の日本語化。
これはページタイトルと入力フォームの部分を日本語化すればいい。

最後にrememberチェックボックスを外すのは、何も考えずに一旦コメントアウトするという作戦で行く。
#パラメータが投稿されてないからエラーになるとかいう可能性があるけど、一旦やってみる。
で、できたのがコレ。


ちなみにソースはこう。

ログイン

<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<%= f.label 'メールアドレス' %>
<%= f.email_field :email, :autofocus => true %>
<%= f.label 'パスワード' %>
<%= f.password_field :password %>
<%= f.submit "ログイン" %>
<% end %>
という感じで当初の目的は達成したので、今回は一旦以上。
次回は余裕があればもうチョットいじってみようかと思います。





2013年2月4日月曜日

deviseの標準画面をカスタマイズする ~bootstrapを当ててみる~


この記事金曜に投稿しようと思ってたのですが、ジョジョ見てたら忘れてました。
#きっとキング・クリムゾンの攻撃をうけたのでしょう。

で、今回から数回はdeviseで自動生成された画面をチョットカスタマイズしましょう、という話。
まずは、はやりのTwitter bootstrapを当ててかっこ良くしようと考えています。
というわけで以下。

1. bootstrapについて
この後bootstrapについて自分の理解を書いています。

Twitter bootstrapとも。
ざっくり言うと、
『見栄えがよくてかっこいい動きをするサイトをつくるためのCSS(ちょっとJavaScript)フレームワーク』
ということだと思う。
じゃ、『フレームワークってなんだよ』、という話ちょっと扱いを間違えると宗教戦争的な発展を見せるので、今はざっくり
『ある目的を達成をしようとする場合は、その目的を達成するのに特徴的な行為とその目的でなくても必要となる一般的な行為を合わせて実行することが多い』
『そのため特徴的な部分の実行に注力できるように、一般的な行為の代行をすると共に行為全体の実行順序制御御を代行してしまう枠組みが効果的である』
『このような枠組みをフレームワークと呼ぶ』
ということでおいておいてください。
#ムロンざっくりで無いことは認識済。

で、↑の定義を今回の話に置き換えてみると、サイトの見栄えを良くするためには大体以下のことをやるはず。
  • かっこいいよく見せるためのCSSの定義(色/フォント/形/背景)
  • フォームにclass属性やid属性の追加
  • JavaScriptでイベントをハンドリングして動的にCSSが適用されるようにする
最終型がファンシーなサイトになるにせよエロサイトになるにせよ、
『CSSを定義し、それが適用されるようにHTMLを記述し、また動的な表現ができるようにJavaScriptを記述する』
という行為は変わらないはず。

そこでCSSフレームワークbootstrapの出番となる。
bootstrapは以下のことをやってくれる。(キャッチーな機能を抽出してるので全部ではないです)
  • 簡単にグリッドレイアウトが組めるようなCSS定義
  • ラベルやボタンなどのフォームがきれいな見栄えになるようなCSS定義
  • 普通なら自分でCSSを定義して自分でJavaScriptを書く必要があるナビゲーションやタブなどが簡単につくれるようなCSS定義&JavaScript

こいつはベンリだ。しかもTwitterが使ってるものだしかっこよくなるのが目に見えてるぜ。
というわけで使ってみましょうというのが今回の話。(なんかやたら前置きが長くなったけど)
#ムロンスライドでも言ってるように、『Twtterみたいな見栄えになっちゃう』、というデメリットはあるが。

2. bootstrapの導入方法
からダウンロードする。
Download Compiledの方を落としてくる。
落としてきたzipファイルを解凍すると
css
img
js
というフォルダがあるはずなので。
コレをrailsのアプリに取り込めばいい。
のだが取り込み方にちとコツ(というか注意)がある。

まず、簡単なJavaScriptの方から。
これはほぼ何も考えずに
{導入しようとしているrailsのアプリフォルダルート}/app/assets/javascripts
においてしまえばいい。
application.jsで
require tree
しているならそれで読み込まれるし、それ外してコントローラ単位にレイアウト作ったりしてるならそこで
<%= javascript_include_tag 'bootstrap.js' %>

とかして読みこめばいい。

次にCSSと画像なのだが、
『こんなもんCSSをassets/stylesheetsにおいて画像をassets/imagesにおけばいいんだろ、ちょろいもんだぜハハハ』
とかやると失敗する。
理由は、
『スタイルシートの中で背景画像を定義する部分においてスタイルシートファイルからの相対パスで画像のURLを指定している』
ため。
bootstrap.css には
  background-image: url("../img/glyphicons-halflings.png");
のような背景画像を読み込む定義があるのだが、↑のようにurlを相対パス(../img・・・)で指定している。
そのため、さっきのような置き方(assets/imagesに配置)とかだとパスが合わなくて画像が読み込めなくなる。

解決策としては以下2点となる。
  • cssの背景画像読込のURLを修正する
  • 画像の配置場所を修正する
で、どこをどう考えても後者の方がラクチンなのでそれを採用します。

で、『フォルダ構造とか一旦いいからとりあえず動かしてみたいよ』という人は
css
img
の二つのフォルダをassets/stylesheetsフォルダにコピーすればいいです。
あとはさっきのJavaScriptと同じように
application.css で
 *= require_tree
やってるなら(たぶん)読み込まれる。

各レイアウトで読み込むなら、
<%= stylesheet_link_tag    "css/bootstrap", :media => "all" %>
<%= stylesheet_link_tag    "css/bootstrap-responsive", :media => "all" %>
みたいなかんじでよいはず。
#今回はこっちでやった。

3. bootstrap当てたらどんな風になるか
beforeがこう。
#前にも出したやつ。

で、当ててみるとこうなった。

うむ、ぜんぜん違う。
パッと見でも

  • フォントが柔らかい
  • テキストボックスが角丸
  • リンクの色が違う
  • チェックボックスの横の文字列が改行してる
  • (あとこの画像だとわからないけど)テキストボックスにフォーカス入れると強調表示される

とかになっている。
次回はこの画面に対して

  • 日本語対応
  • レイアウト調整
  • 余裕があればbootstrapをチョット試してみる

とかをやっていきたい。
一旦以上。

2013年1月31日木曜日

railsの学習書籍 RailsによるアジャイルWebアプリケーション開発

本日は軽めの内容でrailsを勉強するときの書籍について。 
ワタクシは

RailsによるアジャイルWebアプリケーション開発 第4版

という本で勉強しました。

railsの導入からアプリの土台(scaffold)作成、DBアクセス、コントローラ初期記載にViewのカスタマイズ、はてはAjax通信やメール送信などこれを一通りやったらrails初級者は卒業できるんじゃないかと思っています。
一応目次だけさらすと以下のカンジです。

I 部はじめてのRails 
第1 章Rails のインストール
第2 章Hello, Rails !
第3 章Rails アプリケーションのアーキテクチャ
第4 章Ruby 入門

第II 部アプリケーションを構築する
第5 章Depot アプリケーション
第6 章タスクA:アプリケーションの作成
第7 章タスクB:検証とユニットテスト
第8 章タスクC:カタログの表示
第9 章タスクD:カートの作成
第10 章タスクE:もっとスマートなカート
第11 章タスクF:Ajax の追加
第12 章タスクG:チェックアウト!
第13 章タスクH:メールの送信
第14 章タスクI:ログイン
第15 章タスクJ:国際化
第16 章タスクK:デプロイと本番環境
第17 章Depot のふりかえり

第III 部Rails 詳説
第18 章Rails の構造
第19 章Active Record
第20 章Action Dispatch とAction Controller
第21 章Action View
第22 章キャッシュ
第23 章マイグレーション
第24 章ブラウザを使わないアプリケーション
第25 章Rails の依存コンポーネント
第26 章Rails のプラグイン
第27 章これからどうする

ただやっていて思ったのは、実装練習の流れはサクサク進むんだけど、

  • なんでこれをやってるんだっけ?
  • どうしてこれでいいんだ?

という質問に(完全には)答える内容にはなっていないこと。

これは以下の点に起因すると考えている。

  • rubyという言語自体がもつ『プログラムを容易にするための柔軟性』
  • railsというFWがもつ『開発スピード&楽しさを向上させるための支援性』

堅苦しいこと言ったけどようは『効率的な作業やろうとしたらそのやりかたを熟知しなければいけないし、便利なもの使ったら中の挙動はよくわからん』、ということです。

ただいい本であることは間違いないです。
#やってるうちに基本的なことが理解できるベストプラクティス型だと思ってます。

rails力が以前よりもアップした今の自分なら、前よりは内容を理解してプラクティス(というかrailsの理解)ができる…はず。

2013年1月30日水曜日

Railsのdeviseを使ってみる ~自動生成された標準の画面を調べよう7~

とういわけで前回からの残り部分『deviseを自分ちのアプリで(さしあたり)使うにはどうするか』について書きます。

こういうときはどういう使い方をしたいかをとりあえずあげるのがいいのであげます。
以下のカンジ。

  • ログイン前にルートページにアクセスすると自動的にログインページにいく
  • ログイン前はユーザ登録ができる
  • ユーザ登録完了後は[ログインしたものとする, ログインページに行く]のどちらかがいい
  • ログアウトをするとログインページに行く
これだとすると以下の設定でなんとかなるはず。
  1. ルートページをログイン後の初期画面(ポータルとかトップメニューページ等)とする
  2. ルートページにアクセスした際に未ログインであればログイン画面に飛ばすことにする
  3. ログイン画面にログアウトリンクを配置する
1. に関してはroutes.rbの:root_to で設定すればいいので省略。
2. に関してはJavaで言うところのログインチェックフィルター的なもので実現すればいい。
で調べてみたところ、『コントローラの実行前フィルターに認証フィルターを追加すればよい』、らしい。
日本語で書くとわかりにくいが用は認証チェックを行いたい画面のコントローラに

before_filter :authenticate_user userはモデル名

とすればいいらしい。
#認証フィルターの中身やfilterの機構そのものは今度調べてみたい。


今回は試験的に portalというコントローラ&画面のセットを作成してみた&ウチの認証用のモデルがdevise_userであるので
class PortalController < ApplicationController
  before_filter :authenticate_devise_user!

  #portalの初期表示用アクション
  def index
  end
  
  #普通はコレ以外にもデータ操作などの色々なアクションがある
end
のようにした。

3. についてはググったらすぐ出てきた。
ログアウトリンクを表示する部分で。
<%= link_to 'ログアウト', destroy_devise_user_session_path, method: :delete  %>
とすればいい。(うちはモデル名がdevise_userなので↑のようなヘルパーメソッド名になっている)

なので、今回であればポータル画面に↑のリンクを追加すれば良い。

コレmethod名がdeleteになっているのがポイント。
確かにルーティングを見ると

となっているので、DELETEメソッドじゃないと受付無いようになっている。

ちなみに↑の用にリンクを作っても何故かGETメソッドで飛ばしてしまうという事象にそうぐう*1 した人もいたもよう。
#railsとかdeviseのバージョンが関係しているのかな、とか思った。
#リンク生成ならrailsのバージョンによる影響な気もするが。。。

以上でやりたかったことはできたのでまとめると。

【もともとのねらい】

  • deviseの自動生成された画面及び処理を(そこそこ)理解する
  • (あんまり手をかけず≒自動生成されたものを活かして)自分ちのアプリに組み込む


【具体的にどういう仕様にするか】

  • ログイン前にルートページにアクセスすると自動的にログインページにいく
  • ログイン前はユーザ登録ができる
  • ユーザ登録完了後は[ログインしたものとする, ログインページに行く]のどちらかがいい
  • ログアウトをするとログインページに行く

【上記仕様を実現するために何をすればいいか】

  • ルートページをログイン後の初期画面(ポータルとかトップメニューページ等)とする:configのroutes.rbで root :to => "portal#index" を記載する。(無論publicのindex.htmlは削除する)
  • ルートページにアクセスした際に未ログインであればログイン画面に飛ばすことにする:ログインチェックをかけたい画面のコントローラに before_filter :authenticate_devise_user! を追加する。(devise_userは認証用のモデル名なのでモデル名を変えたらそれに合わせる)
  • ログイン画面にログアウトリンクを配置する:認証後の画面に < %= link_to 'ログアウト', destroy_devise_user_session_path, method: :delete  >  を記載する

ということであったらしい。
というわけで長かったdeviseの話も今回で一段落とします。
次回deviseが登場するのは中級編でしょう。そこでは以下のようなことをやるつもりです。

  • ユーザ登録データ制約のカスタマイズ(パスワード文字数は256文字以上じゃないとダメ!とか)
  • 自動生成された画面をカスタマイズする(見栄えを替えるとか自作するとか)
  • 各アクション後の遷移先ページをカスタマイズする(ログアウト後はログイン画面じゃなくてyahooに飛ばしたいんだ! とか)
  • 他のdeviseの機能について(飛ばしたconfirmとか)

一旦以上。


1:『そうぐう』という単語を見るたびにドラえもんの『未知とのそうぐう機』を思い出してしまう。使うとUFOが飛んできて未知との遭遇が体験できるというとんでもない道具だった。

2013年1月29日火曜日

Railsのdeviseを使ってみる ~自動生成された標準の画面を調べよう6~

前回まででGETで取得できる画面はだいたい取得できたので、今回は以下をやります。

  1. 画面遷移の整理(つながりとか)
  2. 自分ちのアプリでどう使えばいいかの手順の整理
今回が終われば自分ちのアプリに組み込めるからいい区切りになるはず。

1. 画面遷移の整理

今まで得た情報を元にすると以下の流れになっている。
#ちっちゃくてすいません。






ルーティングパスとパス消化状況(↑の図で登場してるか)は下記。
HTTPメソッドカラムの冒頭番号は図の遷移の番号と対応。
HTTPメソッドurlルートパラメータ
①GET/devise_users/sign_in(.:format)devise/sessions#new
②POST/devise_users/sign_in(.:format)devise/sessions#create
DELETE/devise_users/sign_out(.:format)devise/sessions#destroy
⑥POST/devise_users/password(.:format)devise/passwords#create
⑤GET/devise_users/password/new(.:format)devise/passwords#new
⑦GET/devise_users/password/edit(.:format)devise/passwords#edit
⑧POST/devise_users/password(.:format)devise/passwords#update
GET/devise_users/cancel(.:format)devise/registrations#cancel
④POST/devise_users(.:format)devise/registrations#create
③GET/devise_users/sign_up(.:format)devise/registrations#new
⑨GET/devise_users/edit(.:format)devise/registrations#edit
⑩PUT/devise_users(.:format)devise/registrations#update
DELETE/devise_users(.:format)devise/registrations#destroy



残ってるのは

  • /devise_users/sign_out
  • /devise_users/cancel
  • /devise_users (DELETEメソッド発行する奴)
の3つだが、

  • sign_out ⇒ ログアウトでしょ
  • cancel ⇒ よくわからないから一旦パス
  • DELETEメソッド発行する奴 ⇒ ユーザアカウント削除でしょ

と思われるのであとはなんとかなりそう。

というわけで次回自分のアプリへの設定手順を整理してdeviseは一段落とする。
#今回で終わらなかった。。。

2013年1月28日月曜日

Railsのdeviseを使ってみる ~自動生成された標準の画面を調べよう5~

というわけで前回の続きで画面を表示させてみる。
残りは
  • 3. ユーザ登録キャンセル
  • 5. パスワード更新画面表示
の2つ。
#前回は間違えて3より先に4をやってしまっていたらしい。

というわけでログインしている状態で上記2つを表示させてみる。


3. ユーザ登録キャンセル

でやってみたところ、起点画面に遷移して
You are already signed in.
と表示されてしまった。
むむむ。整理すると。
  • ログインしていない状態➡ログイン画面(Sing_in)画面へ飛ばされる
  • ログインしている状態➡『おまえはもうじゅうぶんにろぐいんしている』
と言われる。
この挙動から見るにこの画面は、『ログインしていない状態でアクセスするのが正しい』、ように見える。
#エラーメッセージが出るのがログイン済の状態なので。
悩んでもしょうがないのでコードを読んでみることにする。
/devise_users/cancel のアクションは devise/registrations#cancel であり、そのソースは下記。
#うちはRubyMineで開発しているので Command + N のクラス名を検索したところ、 devise/RegistrationController が発見できたのでそれで見た。
#テキストエディタの人はgemのdeviseのフォルダ以下(あるのかどうかもわからんけど)でも探しにいくことになるのでは無いかと推測。
#例によって日本語コメントはコチラで付記。
  
  # GET /resource/cancel
  # Forces the session data which is usually expired after sign
  # in to be expired now. This is useful if the user wants to
  # cancel oauth signing in/up in the middle of the process,
  # removing all OAuth session data.
  # セッションデータ(普通はSign_in後に破棄される)を直ちに破棄します。
  # これはユーザがOAuthのSign_in,Sign_up の処理を中断するのにベンリです。
  def cancel
    # sessionからdevise… というキーのデータ全部消す
    expire_session_data_after_sign_in!
    #registrasion#new へリダイレクト
    redirect_to new_registration_path(resource_name)
  end

うーん、完全に推測だけど以下のカンジだろうか。
  1. 認証方式をOAuthにする
  2. この状態で認証しようとするとdeviseは入力された情報をsessionに溜め込む
  3. その後OAuth認証を行おうとする
  4. で、途中で辞めたくなった場合に中途半端にsessionにOAuthのデータが残るとセキュリティ的にいまいちなので消す
ともあれとりあえずこの画面を後回しにするべき(なんか深くはまりそう&これより先に普通に使う画面について勉強した方がいい)ということだけはわかった。

5. パスワード更新画面

これも『こんなのログイン後にアクセスしたらパスワード変更画面が出るあれだろ、ハハハ』とか思ってアクセスしたところ。

起点画面に遷移して
You are already signed in.
と表示されてしまった。
あべし。

やむなしと思いなしてpassword#editの中身をのぞいてみると。

  # GET /resource/password/edit?reset_password_token=abcdef
  def edit
    self.resource = resource_class.new
    resource.reset_password_token = params[:reset_password_token]
  end

とほぼノーヒント状態。

#ちなみに後でわかるがこれはノーヒントなどではなく、もうクリティカルな回答そのものが提示されていた。

で、さんざん悩んだあげくgoogl先生に聞いてみたところ
コチラのすばらしいブログhttp://blog.livedoor.jp/nizoraul/archives/3622073.html
が同じようなこと(deviseの解説)をなされていたので、読み進めていくと以下の記載が。

そのあとで、自分のメールのユーザー作って、パスワードを綺麗サッパリ忘れます。 
 そのあとで、「Forgot your password?」からメールアドレスを入力して「send me reset password instructions」をクリックすると... 

おおぅなにやら英文のメールが届きました。そのなかの 
Change my passwordというのがあり、クリックするとパスワード変更ページが開きます。URLを見ると?reset_password_token=****のパラメータが設定されてますねぇ。



あー、わかった、この画面って。

  1. パスワードを忘れた人がパスワードリセット申し込み画面でメールアドレスを入力してリセットを申し込む
  2. 入力したアドレスにリセット画面のリンクが届く
  3. 該当リンクの画面を開いてパスワードリセットを行う
ってやつだ。
となるとそのリンクは  devise_users/password/edit?reset_password_token=zzzz
の的なヤツと推測できる。
#さっきのpassword#editの冒頭に思いっきり GET /resource/password/edit?reset_password_token=abcdef って書いてあるじゃん…、というのがクリティカルな回答でした。

でそのtokenはメールに書いてあるんだろうけど、『そのリンクの正当性を確認するためにはサーバ側にもその値を持っていなければならない』、だろうから多分DBに持ってるはず。
➡ DBのdevise_usersテーブルをのぞいてみるとそのものズバリ reset_password_token というカラムがいる。
で、パスワードリセットを実行したレコードにだけreset_password_token の値がセットされている。

ここまでくればやることはただ一つ。
/devise_users/password/edit?reset_password_token={DBに保存してあるトークン}
にアクセス。
#{}内は置換する。

でたー!



やったー。
苦労したので感慨一入だー。

という訳で今回はココまで。
次回は今までわかったこと、とくに画面遷移を整理して、実際に使う場合のやり方を明らかにしよう。

2013年1月27日日曜日

Railsのdeviseを使ってみる ~自動生成された標準の画面を調べよう4~

というわけで前回の続きで自動生成された画面を見ていく。
今回はログインしていなかったので表示できなかった3画面にリベンジする。

4. ユーザ編集画面表示

ログインしていない状態でリクエストするとログイン画面(Sign in)に飛ばされたやつ。

今度はできるだろと思って、ブラウザで localhost:3000/devise_users/edit にアクセスするとなんとエラー画面(rails のよくあるエラー画面)に。

エラー内容を読むと。

  • Devise/registrations#edit でメソッドありませんエラー発生
  • edit.html.erb の9行目で起きた
  • reconfirmableなんてメソッドないよ
ということらしい。
具体的には
<% if resource.class.reconfirmable && resource.unconfirmed_email.present? %>
というコードで落ちてる。
多分resourceは編集対象(つまりログインしている人のユーザデータ)をさすと思うので、resourceのclass(これはチョットわからん)にreconfirmableなんてメソッドないよ、と言ってるのだと推測。
で、エラーメッセージでググってみると、
『Hi それはconfig/intializers/devise.rbで config.reconfirmable = true になっているからさ!』
『Hi それはテーブルに必要なカラムがないからさ!』
的なのがあったのでdevise.rbを見直してみると、config.reconfirmable = false になっていた。
ウーン、なんでだろうと思ってテーブルを作成したmigrationを見直してみると
## Confirmable
# t.string   :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string   :unconfirmed_email # Only if using reconfirmable

とこっちは封印がされている。
で、とりあえずこいつらをテーブルに追加してみる。

空マイグレーション作成
rails g migration AddConfirmableToDeviseUsers

で中身を以下の感じに。
class AddConfirmableToDeviseUsers < ActiveRecord::Migration
  def change
    add_column :devise_users, :confirmation_token, :string
    add_column :devise_users, :confirmed_at, :datetime
    add_column :devise_users, :confirmation_sent_at, :datetime
    add_column :devise_users, :unconfirmed_email, :string
  end
end

でやってみた(ユーザ新規登録➡ユーザ編集)のだがさっきと同じエラーになった。
で、さらにググってみると
『Hi confirmableを使うならconfig.reconfirmable=trueにしてviewを生成するのさ!』
的なメッセージがあったので、
『これひょっとして、config.reconfirmable=trueの状態でview生成して、その後にfalseに設定したんじゃね?』
と思い直して、以下を実施。

  • 一旦deviseのviewを削除
  • config.reconfirmable=false に設定(していることを確認)
  • rails generate devise:viewを実施

で、できたedit.html.erbを見てみると
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
と生成結果が変わっている。
これは期待できる。
➡というわけでやってみた(ユーザ新規登録➡ユーザ編集)


できた。
#左端の青はスクリーンショットとるときに映り込んだうちのmacの背景

ソースは以下のカンジ。

Edit Devise user



(leave blank if you don't want to change it)

(we need your current password to confirm your changes)

Cancel my account

Unhappy?
. Back

というわけで本日は以下のノウハウと以下の課題が。

  • deviseでconfimationを使わない場合は設定ファイルで『設定しない』をやってからviewを作ること
  • そもそもconfirmation機能とはなにかを明らかにすること

長くなったので本日はここまで。
次回は画面表示の続き。

2013年1月26日土曜日

Railsのdeviseを使ってみる ~自動生成された標準の画面を調べよう3~

前回は生成されたview, model, migrationの中身をチラ見した。
今回は実際にリクエストを打って画面を表示させてみよう。

この前のルーティング情報に基づくといきなり表示できそうなのは以下。
#ちなみにHTTPメソッドは全部GET
  • ログイン画面表示 /devise_users/sign_in
  • ユーザ登録画面表示 /devise_users/sign_up
  • ユーザ登録キャンセル /devise_users/cancel
  • ユーザ編集画面表示 /devise_users/edit
  • パスワード更新画面表示 /devise_users/password/edit
  • パスワード??? /devise_users/password/new

順番に画面を表示させてみよう。

1. ログイン画面
ブラウザで localhost:3000/devise_users/sign_in にアクセスすると以下の感じに。















で、表示されているソースを見ると以下のようになっていた。
#例によって日本語コメントはコチラで追記。

Sign in



Sign up Forgot your password?

サマリーすると。

  • ログイン用フォーム
  • ユーザ新規登録へのリンク
  • パスワード忘れた人用のリンク
の3つがこの画面のコンテンツということになる。

2. ユーザ登録(Sing_up)画面

ブラウザで localhost:3000/devise_users/sign_up にアクセスする or ログイン画面のSing upリンクをクリックすると以下の画面が出てくる。
















で、表示されているソースを見ると以下のカンジ。
#例によって日本語コメントはコチラで追記。

Sign up





Sign in Forgot your password?


で、見てみると /devise_users/password/new はパスワード忘れた人用リンクのURLとなっている。

つまり 
  • パスワード??? /devise_users/password/new
の正体は

  • パスワードを忘れた人へ /devise_users/password/new
と思われる。
3. ユーザ登録キャンセル

これはいきなり表示できない気がするけど、とりあえずやってみる。
ブラウザで localhost:3000/devise_users/cancel にアクセスするとさっきのユーザ登録画面(SignUp)が表示された。
まあ、うまく行かないとは思ってた。
一旦後回し。

4. ユーザ編集画面表示


これもログインしていない状態じゃ表示できない気がするけど、とりあえずやってみる。
ブラウザで localhost:3000/devise_users/edit にアクセスすると今度はログイン画面(SignUp)が表示された。
ユーザのデータ修正するならまずログインしなさい、ということでしょう。
一旦後回し。

5. パスワード更新画面表示

これも4.同じ結果なので省略。


6. パスワード???

ブラウザで localhost:3000/devise_users/new 以下のカンジになった。













うむ、ヤハリ正体はパスワードを忘れた人用画面だった。
ソースは以下のカンジ。

Forgot your password?


Sign in Sign up


といったあたりで今回はココまで。
次回はユーザを作ってみて今回表示できなかった画面を表示する予定。