web-k.log

RubyやWebをメインに技術情報をのせていきます

Web Storageの使い方

| Comments

Web Storageとは

Web Storageは、HTML5の周辺APIのひとつで、ブラウザにデータを保存するための仕組みです。データの保存・上書き・削除・全クリアなどの操作は、Javascriptで行います。 Web StorageはCookieとよく似ていますが、Cookieに比べてはるかに大きな容量のデータをブラウザに保存できます。 Web Storageには、sessionStorageとlocalStorageの2種類のストレージが用意されています。どちらもキーと値をペアにしたデータリストをブラウザに保存するkey-value型のデータ保存形式である点は同じですが、データの有効期限などが異なります。 対応している主要なブラウザはIE8以降、Firefox3.5以降、Safari4.0以降です。詳しくはCan I use… Support tables for HTML5, CSS3, etcをご覧ください。

CookieとsessionStorageとlocalStorageの差異

機能 Cookie sessionStorage localStorage
保存容量 4KB 1オリジン当たり5MB(推奨) 1オリジン当たり5MB(推奨)
データの有効期限 指定期限まで有効 ウィンドウやタブを閉じるまで有効 永続的に有効
サーバーへのデータ送信 毎回自動送信 必要時のみ送信 必要時のみ送信
別ウィンドウでのデータ共有 不可

オリジン: プロトコル://ドメイン名:ポート番号 のこと

IE6, 7でローカルストレージを実現

IE6, 7でWeb Storageを利用することはできないが、jStorageというjQueryプラグインを利用することで、ローカルストレージを実現できる。 ただし、保存容量が128KBになるなどWeb Storageに劣るところはある。

Web Storageのメソッドと使い方

Web Storageで提供されているメソッドは、データの保存・取得・指定キーの値削除・全値クリアの4つです。メソッドはsessionStorageとlocalStorageで共通です。

1
2
3
4
5
6
7
8
9
10
11
//データの保存
setItem(key, value)

//データの取得
getItem(key)

//指定キーの値削除
removeItem(key)

//全値クリア
clear()

データの上書きをする場合はもう一度keyとvalueを保存します。メソッドの使い方は以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var storage = localStorage;

//userIdキーに1を保存
storage.setItem('userId', '1');

//userIdキーに新たなvalueをセットし直せば上書き保存
storage.setItem('userId', '2');

//userIdキーの値を取得(2が返る)
storage.getItem('userId');

//userIdキーの値を削除
storage.removeItem('userId');

//ストレージにあるデータをすべてクリア
storage.clear();

localStorage使用上の注意

  1. cookieをブロックしている場合、localStorageが機能しない
  2. cookieを削除するとlocalStorageのデータも消える

より詳細な情報は無職のプログラミング Web Storageについて調べるに記載されています。

Gitでよく使う21コマンドまとめ

| Comments

よく使うgitコマンドをリストアップしてみる

リポジトリ作成: git init

ローカルリポジトリを作成するにはリポジトリ名を指定する。

1
2
$ git init repo
Initialized empty Git repository in /Users/user/repo/.git/

リポジトリの設定: git config

リポジトリ単位の設定は.git/config、ログインユーザ単位の設定は~/.gitconfigに、システム単位は/etc/gitconfigに格納されている。それぞれ、

1
2
3
$ git config キー 値           # リポジトリ単位
$ git config --global キー 値  # ログインユーザー単位
$ git config --system キー 値  # システム単位

にて値を設定出来る。各値はシステム→ログインユーザー→リポジトリ単位の順で読み込まれ、後から読み込んだほうが優先される。現在の設定は

1
$ git config --list

で参照可能。良く設定するキーは

1
2
3
$ git config user.name    # コミットユーザー名
$ git config user.email   # コミットE-mail
$ git config alias.*      # gitコマンドAlias

ファイルをインデックスに登録: git add

gitの場合、ファイルを追加・更新しただけではまだコミット候補ではなく、インデックスに登録する必要がある。

1
2
$ git add .     # カレントフォルダ以降のファイルをすべて登録
$ git add path  # ファイル・フォルダの登録

リポジトリの状態を確認: git status

ワーキングツリー・インデックスの状態を確認する。リポジトリとの現在のファイルの変更・修正状況、コミット候補としてインデックスに登録されているか確認出来る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git status
# On branch source
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   .gitignore
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       source/_posts/2012-11-07-git.markdown
no changes added to commit (use "git add" and/or "git commit -a")

$ git status -s  # status簡易版
 M .gitignore
 ?? source/_posts/2012-11-07-git.markdown

差分を確認する: git diff

リポジトリとの差分が確認出来る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git diff  # ワーキングツリーの差分
diff --git a/.gitignore b/.gitignore
index 4bba145..b4bbc86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,5 @@ source/_stash
source/stylesheets/screen.css
vendor
node_modules
-nbproject/*
\ No newline at end of file
+nbproject/*
+*.swp

$ git diff HEAD  # インデックスとリポジトリ最新との差分
$ git diff master  # masterブランチとの差分
$ git diff master develop  # masterブランチとdevelopブランチとの差分

コミットログの表示: git log

1
2
3
4
$ git log  # 現在のブランチのログ表示
$ git log -p  # 現在のブランチのdiffも含めてログ表示
$ git log -2  # 最新2つのコミットログを表示
$ git log --oneline --graph  # ログをコミット毎に1行フォーマットで表示し、コミットツリーを表示する

ディレクトリ・ファイルの移動: git mv

コミット済みのファイルを移動・リネームする。コマンド実行するとコミット候補としてインデックスに登録される

1
2
$ git mv file1 file2  # file1をfile2にリネームする
$ git mv dir1/file dir2/file  # fileをdir1からdir2に移動する

ディレクトリ・ファイルの削除: git rm

コミット済み、またはインデックスに登録済みのファイルを削除する

1
$ git rm file  # fileを削除する

git rmをオプション無しで実行するとワーキングツリーからファイルが削除されるので、新規追加登録したファイルをインデックスから解除してファイルをワーキングツリーに残しておきたい場合は–cachedを使う

1
2
3
4
5
6
7
8
9
10
11
12
$ git add file
$ git rm --cached file
rm 'file'
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       file

コミットする: git commit

1
2
3
$ git commit  # git addしておいたファイルのコミット。エディタが立ち上がってコメントを入力して保存するとコミットされる
$ git commit -m "コメント"  # コメントを指定してコミット
$ git commit -a  # すべての変更をコミット。ただし、新規ファイルは追加されない。明示的にgit addする必要がある

直前のコミットを変更する: git commit –amend

–amendオプションを指定すると直前のコミットを追加修正したインデックス登録の差分を含めて差し替えることが出来る

1
$ git commit --amend  # (インデックス登録が無ければ)直前のコミットコメントを変更する

インデックス登録してある差分を含めて直前のコミットを変更する

1
2
$ git add file  # fileをインデックスに追加
$ git commit --amend  # addしたfileと直前のコミットをマージして差し替える

ローカルリポジトリ、インデックスを元に戻す: git reset

1
2
3
$ git reset --hard commit_hash  # 指定したコミットにローカルリポジトリ、インデックスを完全に戻す。コミットしていない状態に戻り無かったことになる
$ git reset --soft HEAD~  # 1つ前のコミットに戻す。ワーキングツリー・インデックスファイルは影響しない
$ git reset --mixed HEAD~  # 1つ前のコミットにインデックス・リポジトリを戻す。ワーキングツリーには影響しない

grepする / git grep

インデックスやワーキングツリーに対して検索する

1
$ git grep Text  # Textという文字列を検索する

コミット内容の表示: git show

1
$ git show commit_hash  # コミット内容表示。差分も参照出来る

リモートリポジトリをローカルにコピー: git clone

1
2
3
$ git clone git-uri  # リモートリポジトリをコピー。フォルダ名はname.gitだったらnameになる。リモートリポジトリ名はoriginになる。
$ git clone git-uri dir  # dirフォルダ名でリモートリポジトリをコピー
$ git clone git-uri -o name  # リモートリポジトリ名をnameでコピー

リモートリポジトリの管理: git remote

1
2
3
4
5
6
7
$ git remote  # 登録されているリポジトリ名の表示
$ git remote -v  # リポジトリ名とURIの表示
$ git remote add name git-uri  # リポジトリ名「name」にてgit-uriリモートリポジトリの登録
$ git remote show origin  # originリモートリポジトリの詳細情報表示
$ git remote update  # リモートリポジトリの更新。fetchでも済みそう
$ git remote rm devel  # リモートリポジトリdevelの登録解除
$ git remote prune  # 削除されたリモートブランチの削除

ブランチのマージ: git merge

1
2
$ git merge develop  # 現在のブランチにdevelopをマージ
$ git merge --squash develop  # 現在のブランチにdevelopのコミットを1つにまとめてマージ

リモートブランチにローカルブランチを送信: git push

1
2
3
4
$ git push origin master:master  # リモートリポジトリoriginにローカルブランチ(左のmaster)をリモートブランチ(右のmaster)に送信
$ git push origin master  # リモートリポジトリにローカルブランチmasterを送信
$ git push origin master:testing  # リモートブランチを指定
$ git push origin :testing  # リモートブランチの削除

ローカルリポジトリにリモートリポジトリを取り込む: git pull

1
$ git pull  # originリモートリポジトリをローカルリポジトリに取り込む

ローカルリポジトリにコミットしていない変更がある場合、競合することがあるので、コミットしておくか、git stash saveして一旦ソースを待避してからpullすると良い。

ブランチの管理: git branch

1
2
3
4
5
6
7
8
$ git branch  # ローカルブランチの確認。現在のブランチも確認出来る
$ git branch -r  # リモートブランチの確認
$ git branch -a  # ローカル・リモートすべてのブランチの確認
$ git branch new  # newブランチの作成
$ git branch new base  # baseブランチを起点にnewブランチを作成
$ git branch -m base rename  # baseブランチをrenameブランチに名称変更
$ git branch -d base  # baseブランチの削除。このブランチのみ存在する新しいコミットがある場合は削除されないので安全
$ git branch -D base  # baseブランチの削除。新しいコミットがあっても強制削除する

ブランチのスイッチ: git checkout

1
2
3
4
5
$ git checkout develop  # developブランチにスイッチ
$ git checkout --merge develop  # ワーキングツリー・インデックスで修正があるファイルとスイッチ先ブランチをマージして切り替える
$ git checkout -b new  # 現在のブランチを起点にnewブランチを作成
$ git checkout -f  # 修正したワーキングツリーの修正を元に戻す。一度戻した修正は失われるので注意
$ git checkout commit_hash file  # fileをcommit_hash時点の状態に戻す

未コミットの差分を一時的に保存する: git stash

1
2
3
4
5
$ git stash save  # 一時的に保存する。新規ファイルを保存する場合はgit addしておくこと
$ git stash pop  # 一時的に保存した差分を元に戻す
$ git stash list  # 保存されているキューの一覧を表示する
$ git stash drop stash@{1}  # stash@{1}を破棄。元に戻せないので注意
$ git stash clear  # 保存されているキューを全て破棄。元に戻せないので注意

以上よく使う21コマンドを紹介した。次回はgit rebaseの使い方とgit cherry-pickなど頻繁には使わないが有用そうなコマンドを紹介する。

RSpecで例外のテストをする

| Comments

RSpecで例外のテストするにはlambdaを使用する。 例えば、adminというroleを持つUserのインスタンスに対し、destroyというインスタンスメソッドを実行すると、CannotDestroyAdminUserという例外が出るテストをする。その場合次のように書く。

1
2
3
4
5
it "hoge hoge" do
  @user.roles=(['admin'])
  @user.save
  lambda{@user.destroy}.should raise_error(CannotDestroyAdminUser)
end

Capybaraのドライバ

| Comments

Capybaraは通常Rack:Testと呼ばれるブラウザエンジンを用い、仮想的に画面操作をしています。そのためJavascriptを考慮したテストはできません。 この場合、CapybaraのエンジンをJavascriptが動作する、capybara-webkitやSeleniumというブラウザエンジンに切り替えてテストを行います。 capybara_webkitを使うにはGemfileにcapybara-webkitを追記します。

1
2
3
4
group :test, :development do
  ...
  gem "capybara-webkit"
end

次にenv.rbにJavascriptドライバーを記述します。

1
2
Capybara.default_selector = :css
Capybara.javascript_driver = :webkit

これでJacascripがテストで動作する環境ができました。Javascriptを動作させたいところにRSpecだと

1
2
3
it "hoge hoge", js: true do
  ...
end

Cucumerだとシナリオに@javascriptタグを付加します。

1
2
@javascript
シナリオ: hoge hoge

ブラウザ別のテストを実施したいときはSeleniumドライバーを使います。 SeleniumドライバーはFirefox、InternetExplorer、GoogleChromeを実際に起動させてテストします。 SeleniumはCapybaraに同梱されているので、ブラウザエンジンを切り替えるだけで使用可能になります。

1
Capybara.default_driver = :selenium

デフォルトではFirefoxが起動するので、FirefoxではなくChromeを使用したいときには以下をCapybara.default_driverの前に記載します。

1
2
3
Capybara.register_driver :Selenium do |app|
  Capybara::Driver::Selenium.new(app, browser: :chrome)
end

Seleniumドライバーは実際のブラウザを起動するのでほとんどのJavascriptをテストできますが、オーバーヘッドが大きくなります。 従って、必要なテストにのみSeleniumドライバーを利用するようにします。 RSpecのフィルタで切り替える方法を載せます。この方法は特定のサンプルのみブラウザの切り替えを行います。 フィルタ機能を設定するには、spec_helper.rbにSelenium用のフィルターを用意します。

1
2
3
4
5
6
7
8
9
RSpec.configure do |config|
  ...
  config.before(:all, selenium: true) do
    Capybara.current_driver = :selenium
  end

  config.after(:all, selenium: true) do
    Capybara.use_default_driver
  end

あとはSeleniumを実行したいサンプルにseleniumタグを追加します。

1
2
3
describe "hoge hoge", selenium: true do
  ...
end

ActionScript 3.0

| Comments

ActionScript とは

ActionScript は Adobe Flash Player や Adobe AIR のランタイム環境用の開発言語です。 開発環境には Adobe Flash CS6(オーサリングツール)/Adobe Flex Builder(統合開発環境)/Adobe Flex(コマンドラインのコンパイラ)がありますが、 有償であったりコマンドラインでコンパイルしなければならないので、 ここでは無償の統合開発環境である FlashDevelop を使用しています。 ActionScriptで書かれたコードを上記環境でコンパイルすることにより、SWF(Shockwave Flash file)ファイルが生成されます。 このSWFファイルをウェブページなどに組み込めば、ランタイム環境で動作させることができます。

ActionScript には ActionScript 1.0/2.0/3.0 の各バージョンがあり、2.0からオブジェクト指向言語になっています。 ECMAScriptをベースに作られているため、Javascriptに似ており。また、オブジェクト指向言語になり、Javaにも似ている言語となっています。

文法

基本的なことと気になったことについて書いていきます。

変数宣言

var 変数名:型 で宣言します。

1
2
3
4
5
var num1:Number; //小数点付きまたは小数点なしの値を含むすべての数値
var num2:int; //整数
var num3:uint; //符号なし整数
var boo:Boolean; //true か false の2値
var str:String; //文字列

などがあります。

文字列

文字列はシングルクォートかダブルクォートを使って定義します。\n(改行)などの特殊文字を入れることもできます。

1
var str:String = "kaigyou¥n"

文字列を操作するメソッドも用意されており、Rubyみたいに使えます。

1
2
3
4
5
6
7
var str:String = "FLASH";
trace(str.split(""));//traceを使用すると開発ツール上で実行したときに出力欄に表示される
//>>F,L,A,S,H
trace(str.length);//文字列の長さ
//>>5
trace([str, "TEST",].join(" "));//配列要素間を空白で結合
//>>FLASH TEST

上記コードだけでは実行できないので、以下のようにコードを.asファイルに記載して実行(FlashDevelopではF5)します。

1
2
3
4
5
6
7
8
9
10
11
12
package 
{
  import flash.display.Sprite;
  public class Main extends Sprite 
  {
      public function Main():void 
      {
          var str:String = "FLASH";
          trace(str.split(""));
      }
  }
}

配列

配列用の型があるので、Arrayで宣言します。

1
2
var arr:Array;
arr = [a,b,c];

配列も文字列と同じようにメソッドが用意されており、操作できます。

1
2
3
4
5
6
7
var arr:Array = ['a','b','c'];
arr.push('d'); //末尾にデータ追加
trace(arr);
//>>[a,b,c,d]
arr.pop(); //末尾のデータ取り出し
trace(arr);
//>>[a,b,c]

ハッシュ

Object型を使って、ハッシュを作ります。Object型はすべてのクラス定義の基本クラスです。

1
2
3
4
var hash:Object = new Object();
hash["program"] = "ActionScript"
trace(hash["program"]);
//>>ActionScript

ブロックスコープ

変数は関数単位で管理しており、ブロック変数として宣言したつもりでも関数スコープになっています。(withを使えば、実現できるらしいです。)

1
2
3
4
5
6
7
//多重定義でコンパイルエラー
for ( var i:Number = 0; i < 1; i++) {
  i = 1;
}
for ( var i:String = 0; i < 1; i++) {
  i = 'a';
}

例外

Javaのように例外をキャッチできます。

1
2
3
4
5
try {
    //例外が検出したい処理
} catch (e:Error) {
    // 例外処理
}

クラス

以下のようにクラス定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package パッケージ名 { // パッケージ名省略可
    import パッケージ.クラス名;
    
    public class Main(クラス名、仮にMain) extends 親クラス名
    {
        // 変数宣言
        アクセス修飾子 var プロパティ名:プロパティの型;
        
        // コンストラクタ
        アクセス修飾子 function Main()//コンストラクタ名
        {
            処理1
        }
        
        // メソッド1
        アクセス修飾子 function メソッド名1(引数1:型 = デフォルト値):戻り値の型
        {
            処理2
        }
    }
}

表示リスト

ActionScript 3.0 で構築されたアプリケーションには、表示リストと呼ばれるオブジェクトの階層があります。

1
2
3
4
5
ステージ //Stageクラス
  - SWFファイルのメインクラスのインスタンス //自分で定義したクラス
    - 表示オブジェクト //TextFieldなど
    - 表示オブジェクトコンテナ //Spriteクラス
      -(表示オブジェクト/表示オブジェクトコンテナ)
  • ステージ

表示オブジェクトの基本コンテナ、各アプリケーションには1つのStageオブジェクトがあり、この中に画面の表示オブジェクトがすべて含まれます。 ステージは、表示リスト階層の最上位にあたります。 それぞれのSWFファイルには関連するActionScriptクラスがあり、これがSWFファイルのメインクラスと呼ばれます。 SWFファイルのメインクラスは、Spriteクラスを拡張して定義します。 SWFファイルが Flash Player または Adobe AIR 上で開かれると、SWFファイルのメインクラスのコンストラクタ関数が呼ばれ、 作成されるインスタンスがStageオブジェクトに子として追加されます。

  • 表示オブジェクト

ActionScript 3.0 では、アプリケーション内で表示される全てのエレメントタイプは、表示オブジェクトです。

  • 表示オブジェクトコンテナ

表示オブジェクトコンテナは特殊な型の表示オブジェクトです。 表示オブジェクトコンテナ(単にコンテナともいいます)は、それ自体が表示オブジェクトコンテナ/表示オブジェクトを子オブジェクトに含むことができます。 表示オブジェクトコンテナに子オブジェクトを追加するには addChild関数を使います。 表示オブジェクトコンテナとなりうる表示オブジェクトは Stage/MovieClip/Spriteクラスです。

Hello ActionScript

Flash上に「Hello ActionScript」と文字列を表示させます。

1
2
3
4
5
6
7
8
9
10
11
12
13
package hello
{
  import flash.display.Sprite; //画面表示の基本クラスのインポート
  import flash.text.*; //テキスト系のクラス
  public class Main extends Sprite
  {
      public function Main (){//コンストラクタ
          var textField:TextField = new TextField();//入れ物確保
          textField.text = "Hello ActionScript";//文字列挿入
          addChild(textField);//textFieldをSpriteクラスに追加して表示
      }   
  }
}

参考

i18n(Internationalization)

| Comments

Railsでi18nを使った多言語対応について調べたので、それについてまとめる。 今回は多言語対応の一般的な話をまとめる。

多言語対応について

ソフトウェアを多言語対応するときの工程として

  • i18n(Internationalization:国際化)
  • l10n(Localization:地域化)
  • m17n(Multilingualization:多言語化)

の各ステップがある。 名前の由来は最初と最後の文字と間の文字数からきている。i18nだとInternationalizationの最初のiと最後のn、その間に18文字あることからきている。

「i18n」とは、ソフトウェアに技術的な変更を加えることなく、多言語、多地域に対応させる枠組みを作っておくことである。 i18nに対応すると次に各言語においての対応を実装していく必要がある。それが「l10n」で、特定の1言語で必要とされる言語特有の機能等を実装し、対応させることである。 多言語に渡ってl10nの対応をし、利用者の言語に合わせて切り替えて表示できる状態が「m17n」対応となり、多言語対応となる。

Railsにおける多言語対応

Railsにはi18nの機能が標準でついており、利用することで多言語対応ができる。 詳しくは次回以降で記載する。

参考

Railsのbefore_filterとメソッド返り値

| Comments

before_filterで実行したメソッドがfalseをreturnしたらどうなるか、気になったのでメモ。

確認した環境はRuby 1.9.3p194, Rails 3.2.8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ApplicationController < ActionController::Base

  before_filter :false_filter
#  before_filter :redirect_filter
  before_filter :true_filter

  def false_filter
    return false
  end

  def redirect_filter
    redirect_to root_path
  end

  def true_filter
    return true
  end

true/falseだからといって特に何も起こらなかった。falseでもそのまま次のbefore_filterが実行されたり、filter後に控えているコントローラのメソッドが実行された。

before_filterやafter_filterはメソッドを優先して実行するかどうかを決めているだけであり、メソッドの返り値を受け取ってどうこうするというモノではないようだ。

ただし、before_filterで呼んだメソッドの中にrenderやredirect_to、raiseなどがあると、その後に控えている他のbefore_filterや以降のコントローラのメソッドは実行されない。

なお、rails:3168によると、rails 1.xではfilterにfalseが返ると、そこで処理が止まっていたようです。

Nexus 7(Jelly Bean)のUser AgentとCSS 3D Transforms対応状況を調べてみた

| Comments

今週末10/6にNexus 7が届いて楽しくいじり倒していたのだが、ブラウザでの以下の2点の大きな変更が気になっていた。

  • Android 4.1 Jelly BeansからFlash Playerが未サポートになってGoogle Playから新規インストール出来なくなった。
  • 標準ブラウザがChromeに変わった

Flashが使えなくなったことでHTML5で対応する必要がでてくる(ていうかHTML5で対応出来ないと詰む)ことが多くなる。なので、各ブラウザアプリUser AgentとCSS 3D Transformsの挙動を実際に試してみる。

User Agent

ブラウザ User Agent
Chrome Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03S) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19
Dolphin Mozilla/5.0 (Linux; U; Android 4.1.1; ja-jp; Nexus 7 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30
Firefox Mozilla/5.0 (Android; Tablet; rv:15.0) Gecko/15.0 Firefox/15.0.1
Sleipnir Mozilla/5.0 (Linux; U; Android 4.1.1; ja-jp; Nexus 7 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30
OperaMobile Opera/9.80 (Android 4.1.1; Linux; Opera Tablet/ADR-1207201819; U; ja) Presto/2.10.254 Version/12.00

ICSの標準ブラウザでのUser Agentは後半が「AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30」になっているのでDolphinとSleipnirはJelly Beanになっても以前の標準ブラウザと同一エンジンで同じ挙動を示しそうな感じだ。

Firefoxはバージョン番号が含まれてないのでJelly Beanかどうか判断出来ないという結果に。Flash未サポートかどうかUser Agentで簡単に振り分けられなくなっちゃうので、出来ればバージョン番号も付加して改善して欲しいところ。

次にCSS 3D Transformsの挙動を見てCSS3の対応状況を見てみる。

CSS 3D Transforms

挙動の確認は6枚の画像をサイコロ風に並べたCube型に配置してFPSの様な視点を作ってグリグリ回して確かめた。結果は以下の通り

ブラウザ 対応状況 挙動
Chrome CSSは認識しているがグリグリ動かすと画像落ちする
Dolphin FPSの視点でグリグリOK
Firefox 描写は出来て、画像落ちはしないが、使い物にならないくらい激重
Sleipnir Dolphinと同じでOK
OperaMobile × CSS/3D is not supported. と出る。3D表示出来ない

ちなみにPCではFirefoxとChromeはWin/Mac共PCにGPU搭載されていればHWアクセラレーションがきちんと動作して問題なく動作する。

結果

△は(アニメーションしないならもしかしたら使える時もあるかもしれないが)実用不可なのでDolphinとSleipnirくらいしかまともに動かない。ICS標準ブラウザでは動作していたのにJelly BeanでChrome for Androidに変わったことでCSS 3D Transformsに関しては退化してしまったことになる。早急の改善をChrome for Androidにお願いしたい。

FabricationとCucumberの連携

| Comments

前回Fabricationの続きで、Cucumberと連携する方法についてここに記載します。

Cucumber

インストール

step_definitionsフォルダに便利な cucumber_steps を生成してくれるツールがgemの中にパッケージ化されている。 Gemfileのdevelopment環境にcucumber系を含めている必要がある。

1
2
$ rails generate fabrication:cucumber_steps
# => create  features/step_definitions/fabrication_steps.rb

Step Definitions

WidgetモデルのFabricatorが定義されていれば、下記のように書くだけでFabricateできる。

1
Given 1 widget

Widgetモデルの属性を指定してFabricateすることもできる。

1
2
3
4
Given the following widget:
  | name      | widget_1 |
  | color     | red      |
  | adjective | awesome  |

複数も可

1
Given 10 widgets

属性付きで複数Fabricateする。

1
2
3
4
5
Given the following widgets:
  | name     | color | adjective |
  | widget_1 | red   | awesome   |
  | widget_2 | blue  | fantastic |
  ...

既にFabricateされた”widget”に”wockets”を所属させる。

1
And that widget has 10 wockets

既にFabricateされた”widget”に”wockets”を属性を与えて所属させる。

1
2
3
And that widget has the following wocket
  | title    | Amazing |
  | category | fancy   |

既にFabricateされた”widget”と”wockets”を関連付ける。

1
And that wocket belongs to that widget

データベースにいくつのオブジェクトが保持されているか検証する。

1
Then I should see 1 widget in the database

オブジェクトの中身も検証できる。

1
2
3
4
Then I should see the following widget in the database
  | name  | Sprocket |
  | gears | 4        |
  | color | green    |

Transforms

cucumberのステップでテーブルを変換できる。縦横のテーブルでカラムの値を再配置できる。 spec/fabricatorsフォルダにおいておけば、何とでも設定しておける。

例として、全てのフィールドの”company”に変換の定義をする。lambda には返り値の属性をセットしたい文字列を置く。 その結果、”company”のインスタンスオブジェクトが生成される。

1
Fabrication::Transform.define(:company, lambda{ |company_name| Company.where(name: company_name).first })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Scenario: a single object with transform to apply
  Given the following company:
    | name | Widgets Inc |
  Given the following division:
    | name    | Southwest   |
    | company | Widgets Inc |
  Then that division should reference that company

Scenario: multiple objects with transform to apply
  Given the following company:
    | name | Widgets Inc |
  Given the following divisions:
    | name      | company     |
    | Southwest | Widgets Inc |
    | North     | Widgets Inc |
  Then they should reference that company

divisions を生成したときに、lambdaによって”company”オブジェクトに渡されている。

特定のモデルのスコープにだけ適用したい場合は、only_forを使う。

1
Fabrication::Transform.only_for(:division, :company, lambda { |company_name| Company.where(name: company_name).first })

Fabrication

| Comments

ここでは、Fabrication のサイトを日本語に直して、自己解釈して補完しながら説明していきます。

Fabricationとは

これはオブジェクト生成ライブラリで、 オブジェクトの概略だけを定義し、素早くオブジェクトを使うことができるものである。 サポートしているオブジェクトタイプは以下のものなどがある。

  • ActiveRecord Models
  • Mongoid Documents
  • Sequel Models
  • DataMapper Resources
  • ・・・

設定

Gemfile に Fabrication を記載し、bundle install すれば使える

1
gem 'fabrication'

デフォルトでは以下にFabrication関連のソースを置くと、自動ロードされる。

1
2
spec/fabricators/**/*fabricator.rb
test/fabricators/**/*fabricator.rb

設定を変更したい場合は以下のように、Fabrication.configureで設定変更できる。

1
2
3
4
Fabrication.configure do |config|
  config.fabricator_path = 'data/fabricators' #Fabrication関連の定義を置くパス
  config.path_prefix = Rails.root #ファイルシステムへの許可範囲
end

引数

1
2
class Person; end
Fabricator(:person) #引数がFabricatiorオブジェクトになる。クラス名のシンボルである必要がある
1
Fabricator(:adult, from: :person) #from: :symbolized_class_nameのクラス名を変えて:adultというFabricatiorオブジェクトが定義できる

属性

Fabricator ブロックには変数が必要ではないが、1つ提供される。属性リスト作成時に、宣言もされる。

1
2
3
4
Fabricator(:person) do
  name 'Greg Graffin'
  profession 'Professor/Musician'
end

属性には変数を渡すことができる

1
2
3
4
Fabricator(:person) do
  name { Faker::Name.name }
  profession { %w(Butcher Baker Candlestick\ Maker).sample }
end

属性は処理順に宣言され、上記フィールドのブロック変数を用いることができる。

1
2
3
4
Fabricator(:person) do
  name { Faker::Name.name }
  email { |attrs| "#{attrs[:name].parameterize}@example.com" }
end

予約語

予約語名をブロック変数と一緒に使うことで属性として参照できる

1
2
3
Fabricator(:person) do |f|
  f.alias 'James Bond'
end

関連

他のFabricatorに関連付ける場合は、属性名を書くだけでいい。 これで、「belongs_to」の関連を表現できる。

1
2
3
4
5
6
7
Fabricator(:person) do
  vehicle
end
↑↓等価
Fabricator(:person) do
  vehicle { Fabricate(:vehicle) }
end
1
2
3
4
5
6
7
Fabricator(:person) do
  ride(fabricator: :vehicle)
end
↑↓等価
Fabricator(:person) do
  ride { Fabricate(:vehicle) }
end

countパラメータを使うことで、配列オブジェクトを生成できる。

1
2
3
4
Fabricator(:person) do
  open_souce_projects(count: 5)
  children(count: 3) { |attrs, i| Fabricate(:person, name: "Kid #{i}") }
end

継承

他の Fabricators から属性を継承する場合は、「:from」 を使う

1
2
3
4
5
6
7
Fabricator(:llc, from: :company) do #クラスの属性とその値を全て継承する
  type "LLC"
end

Fabricator(:llc, class_name: :company) do # class_name: でクラスの属性のみを継承する
  type "LLC"
end

初期化

オブジェクトの初期化を通常の方法でしてほしくないときは、 initialize_withを以下のようにオーバーライドすればよい。

1
2
3
4
Fabricator(:car) do
  initialize_with { Manufacturer.produce(:new_car) }
  color 'red'
end

コールバック

Fabricationのビルドにフックするには、after_buildafter_create を使う。

1
2
3
4
Fabricator(:place) do
  after_build { |place| place.geolocate! } #ビルド後=保存する前
  after_create { |place| Fabricate(:restaurant, place: place) } #保存した後
end

オブジェクトに引数を与えたときのコンストラクタでコールバックするときは、on_initを使う。

1
2
3
Fabricator(:location) do
  on_init { init_with(30.284167, -81.396111) }
end

コールバックはスタックになっているので、並列にFabricatorを宣言できるし、継承しても大丈夫。

エイリアス

Fabricatior呼び出し時に:aliasesオプションをつけるとエイリアスが付けれる。

1
Fabricator(:thingy, aliases: [:widget, :wocket]) #Fabricateを :thingy,:widget, :wocketどれでも呼び出せる

一時属性

Fabricator内で一時属性を変数として持てるが、クラス生成時にはセットされない。 一時属性は、クラスが生成されるまでの間は普通の属性と同じように扱えるが、生成時に取り除かれる。

1
2
3
4
5
6
7
8
9
10
Fabricator(:city) do
  transient :asian
  name { |attrs| attrs[:asian] ? "Tokyo" : "Stockholm" }
end
Fabricate(:city, asian: true)
# => <City name: 'Tokyo'>

Fabricator(:the_count) do
  transient :one, :two, :three #複数定義可
end

リロード

Fabricationがロードされた状態にリセットする

1
Fabrication.clear_definitions

基本

Fabricateオブジェクトを作成する簡単な方法は、クラス名を渡すだけでいい。

1
Fabricate(:person)

これで、PersonのインスタンスがFabricatorとして定義される。 Fabricator作成時に、引数としてハッシュを渡せば、属性の追加や、上書きができる。

1
Fabricate(:person, first_name: "Corbin", last_name: "Dallas")

Fabricating With Blocks

Fabricateのブロックの引数にハッシュ値を渡せば、オブジェクト生成時に定義され利用できる。

1
2
3
4
Fabricate(:person, name: "Franky Four Fingers") do
  addiction "Gambling"
  fingers(count: 9)
end

ビルド

データベースにオブジェクトを持続させたくないときは Fabricate.build を使う。

1
Fabricate.build(:person)

下記のように、Fabricate.build内でFabricateが呼ばれていてもオブジェクトは持続せず、buildのときの動作と同じになる。

1
2
3
Fabricate.build(:person) do
  cars { 2.times { Fabricate(:car) } }
end

属性のハッシュ

オブジェクトを生成せずに、属性だけを生成してハッシュで返したい場合は以下のようにする。

1
Fabricate.attributes_for(:company)

Sequences

Sequencesはそのプロセスにおいての、ユニークな連続した数値が得られる。 Sequencesは指定がなければ、0からはじまる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Fabricate.sequence #実行するたびにインクリメントされていく
# => 0 #1回目
# => 1 #2回目
# => 2 #3回目

Fabricate.sequence(:name) #引数を渡せば独自の数値でインクリメントされる
# => 0
# => 1
# => 2

Fabricate.sequence(:number, 99) #第2引数の数値は開始の数値
# => 99
# => 100
# => 101

Fabricate.sequence(:name) { |i| "Name #{i}" } #ブロックで渡してもインクリメントされる
# => "Name 0"
# => "Name 1"
# => "Name 2"

Fabricate(:person) do #例
  ssn { sequence(:ssn, 111111111) }
  email { sequence(:email) { |i| "user#{i}@example.com" } }
end
# => <Person ssn: 111111111, email: "user0@example.com">
# => <Person ssn: 111111112, email: "user1@example.com">
# => <Person ssn: 111111113, email: "user2@example.com">

Rails 3

Rails 3でFabricatorsをモデル生成時に一緒に生成したい場合は、config/application.rb に設定を書く。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# rspecの場合
config.generators do |g|
  g.test_framework      :rspec, fixture: true
  g.fixture_replacement :fabrication
end
# test/unitの場合
config.generators do |g|
  g.test_framework      :test_unit, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: "test/fabricators"
end
# minitestの場合
config.generators do |g|
  g.test_framework      :mini_test, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: "test/fabricators"
end

上記設定後、下記コマンドでFabricationのファイルができる。

1
2
3
4
$ rails generate model widget #コマンド
spec/fabricators/widget_fabricator.rb
 Fabricator(:widget) do #中身
 end

参考リンク