ページ

2013/08/29

[書評]達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ
著者 : ミック
翔泳社
発売日 : 2012-03-16
ブクログでレビューを見る»
DB設計の基礎知識と実践的なノウハウをわかりやすく解説した本。
SQLを使うだけでなく、テーブル設計などを始めたら読んでおきたい。

第1章のタイトル「データベースを制するものはシステムを制す」は
システムエンジニアの金言と言っても過言ではないでしょう。
DB設計がダメだとシステム開発の難易度が跳ね上がるので
DB設計者には本書の内容を身につけておいて欲しいですね。
翔泳社刊行物Q&A:達人に学ぶDB設計 徹底指南書 正誤表

データベース初心者がSQLを極めるためのオススメ書籍6冊 | DevAchieveのうちの1冊です。
達人に学ぶ SQL徹底指南書はまだ読んでいないですが、次あたりに読もうと思います。

さて、達人に学ぶDB設計 徹底指南書の内容ですが、まずは目次を見ましょう。
第1章 データベースを制する者はシステムを制す
第2章 論理設計と物理設計
第3章 論理設計と正規化 ~なぜテーブルは分割する必要があるのか?
第4章 ER図 ~複数のテーブルの関係を表現する
第5章 論理設計とパフォーマンス ~正規化の欠点と非正規化
第6章 データベースとパフォーマンス
第7章 論理設計のバッドノウハウ
第8章 論理設計のグレーノウハウ
第9章 一歩進んだ論理設計 ~SQLで木構造を扱う

第1章はDB設計の重要性と3層スキーマ(外部スキーマ、概念スキーマ、内部スキーマ)について解説しています。
概念スキーマの大切さについて論じられていて、わかるわーってなりました。変更時に残念な事になるからね。

第2章は論理設計と物理設計の流れ、考えるべきことについて解説しています。
バックアップとリカバリはほとんどやったことがないので難しそうだなぁ(そして責任重大だなぁ)と思いました。
フルバックアップの欠点としてサービスの停止が必要とありましたが、
停止しなくても MySQL で InnoDB ならmysqldump --single-transaction
テーブルをロックすること無くバックアップを取ることができるので違うのかな?と思いました。
余談ですが、初めてフルバックアップするときはサービスは停止できないし、
テーブルがロックされたら参照/更新できなくなってとんでもないことになるしで
マニュアルやエキスパートのためのMySQL[運用+管理]トラブルシューティングガイドを読んで
mysqldump --single-transactionで良いんだよなとドキドキしながら実行したことを思い出します。
MySQL :: MySQL 5.1 リファレンスマニュアル :: 7.12 mysqldump — データベースバックアッププログラム
更に余談ですが、あの本って漢(オトコ)のコンピュータ道の中の人が書いた本だったんですね。

第3章と第4章は正規化とER図ということで、設計にあたっての基礎知識を解説しています。
このへん、1冊の書籍としてまとまっているのはありがたいですね。
DB設計の基礎知識を身につけさせたかったら、コレ1冊渡してあげればなんとかなりそうです。

第5章は正規化とパフォーマンスのトレードオフ関係について解説しています。
論理設計は大事だけど、論理設計には物理設計の知識が必要ってことでした。
どんなに綺麗に正規化しても物理設計から離れてはいけないのだ、と。
正規化しすぎると JOIN が増えてパフォーマンスに問題が出るからバランスとるのが難しいですね。

第6章はデータベースのパフォーマンスについて解説しています。
B-tree インデックスは名前だけしか知らなかったので勉強になりました。
統計情報は意識したことないし、InnoDB なら基本的に意識しなくて良いみたい。
漢(オトコ)のコンピュータ道: 大人のためのInnoDBテーブルとの正しい付き合い方。

第7章と第8章は論理設計のバッドノウハウとグレーノウハウについて解説しています。
自分で体験できる論理設計の場面は限られているのでこのようなケーススタディは勉強になります。
そんなに規模が大きくないとバッドノウハウでも何とかなっちゃったりするので
何がバッドかどうかというのを知っておくのは良いですね。

第9章は応用編でリレーショナルデータベースが苦手とされてきた木構造について解説しています。
隣接リストモデルは子が親の ID などを持つことになるかと思いますが、 SQL がかなり複雑になりますね。
葉(leaf)から根(root)を探そうと思うと
自己結合するクエリをループで複数回実行することになって微妙だったことがありました。
入れ子集合モデルや入れ子区間モデル、経路列挙モデルが紹介されていますが、どれも大変そうなイメージです。
まず、これらのモデルを知らない人がカラムの意図を理解することができなそうで大変そうだと思いました。
入れ子区間モデルとか良さそうでしたけど親や兄弟を探すのとかどうやるんでしょうね?
あと、例えば Twitter のリプライのツリーみたいに必ずしも親を持つわけではない場合に
親がいるかどうかの判定がめんどくさそうなのと、その値のマッピングが難しそうだと思いました。
やっぱりそういうのは隣接リストモデルの方がいいんでしょうね。Twitter のテーブル定義が見てみたいですね。

著者のミックさんの書いた解説が Web 上にあるので気になったら読んでみると良いと思います。
SQLで木と階層構造のデータを扱う(1)―― 入れ子集合モデル
SQLで木と階層構造のデータを扱う(2)―― 経路列挙モデル
SQLで木と階層構造のデータを扱う(3)――入れ子区間モデル
他にも色々解説を書いているので SQL ある程度出来る人は読むと勉強になると思います。
リレーショナル・データベースの世界

おわりに

感想だけで結構な文量になるあたり、オススメだと思います。学ぶことが多かった1冊です。

2013/08/25

[書評]HTTPの教科書

HTTPの教科書
著者 : 上野宣
翔泳社
発売日 : 2013-05-25
ブクログでレビューを見る»
さらっと読めて、内容もまさにHTTPの教科書といった本。
HTTP関連の書籍といえば「Webを支える技術」もあるが、
こちらはどちらかと言うとネットワークとかプロトコル寄り。
あと、イラストなどもあり読みやすいので眠くならない(^_^;)
ずっとレビュー書いてなかったから少しどんな本だったか忘れ気味…。
SPDY についても解説していると話題になっていたので買いました。
簡単に概要がわかるのでオススメかと思います。
他にも Comet や WebSocket も合わせて解説してありました。

一点、ハッシュ化を暗号化と説明しているのが気になりましたが、
基本的にはわかりやすくて新人に読ませたいなぁと思いました。
というか僕が新人の時に読みたかったかも。
バーチャルホストとか知らなくて何言ってるかわからなかったことがあったので。

あと、Webを支える技術を引き合いに出してるけど悪い本じゃないよ。
HTTPの教科書が教科書ならWebを支える技術はどちらかというと辞書的だから眠くなっちゃうだけだよ!
REST API とか作る人は一回読んでおいた方が良い本です。オススメ。書評書いてないけど。

2013/08/24

[Android]Homeボタンを押されたことを検知する

Evernote を久しぶりに開いたらネタが転がっていたので記事にしておきます。

参考: Android開発 ホームボタン押下やアプリ切り替わりを検知する - えんたつの記録

Intent#ACTION_CLOSE_SYSTEM_DIALOGS | Android Developers
BroadcastReceiver を登録すれば
Home ボタンが押下されたことを検出することができます。

Intent#ACTION_CLOSE_SYSTEM_DIALOGS の特徴

  • ホームボタンが押された事自体に反応する
  • アプリがバックグラウンドにいても反応する
  • 戻るボタンが押された場合には反応しない(※戻るボタンが押された事は別の方法で取得できる)
(※ [Android]Backボタンを押されたことを検知する | DevAchieve)
参考元では Intent#ACTION_CLOSE_SYSTEM_DIALOGS の問題点として
「アプリがバックグラウンドにいても反応してしまう」ことを挙げていましたが
register したなら unregister すれば良いんじゃないの?と試してみると
うまくいったのでこの点は解消されました。

ということで onPause とかで unregisterReceiver しましょう。
private HomeButtonReceiver mHomeButtonReceiver;

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // ホームボタン押された時の Reciever の登録
    mHomeButtonReceiver = new HomeButtonReceiver();
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    registerReceiver(mHomeButtonReceiver, filter);
}

private class HomeButtonReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
        Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show();
        finish();
    }
}

@Override
public void onPause(){
    super.onPause();
    unregisterReceiver(mHomeButtonReceiver);
}

feedly の Android アプリは毎回リフレッシュしないで
ホームボタンとかバックボタンで閉じられたことを検知したら
次回起動時にリフレッシュすればいいんじゃないですかね。
今の onPause → onResume で毎回リフレッシュは使いづらいですよ…。

[Android]Backボタンを押されたことを検知する

[Android]Homeボタンを押されたことを検知するのおまけ。

戻るボタンを押した時に onKeyDown や onKeyUp でイベント拾って
処理することあるけど Android 2.0 以前では onKeyDown で finish() して
Android 2.0 以降は onKeyUp で finish() する処理になっていたみたい。
Activity#onKeyDown(int, KeyEvent) | Android Developers
Activity#onKeyUp(int, KeyEvent) | Android Developers
参考
ハードキーフックの方法 - 明日の鍵

確実なのはシステムに KeyEvent が送られる dispatchKeyEvent で何かを処理する事。
@Override
public boolean dispatchKeyEvent(KeyEvent event){
    if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
        // 戻るボタンが押された!
    }
    return super.dispatchKeyEvent(event);
}
キーダウンとキーアップの両方で呼ばれるので注意。
Activity#dispatchKeyEvent(KeyEvent) | Android Developers

戻るボタンのみイベントが取れれば良ければ onBackPressed が一番簡単。
@Override
public void onBackPressed(){
    super.onBackPressed();
}
super.onBackPressed(); は finish() するだけのメソッドなので
こいつをコメントアウトして好きな処理を書けば良い。
Activity#onBackPressed() | Android Developers

ちなみに呼び出し順は以下のとおり。
Activity#dispatchKeyEvent
Activity#onKeyDown
Activity#dispatchKeyEvent
Activity#onKeyUp
Activity#onBackPressed

[Android]失敗しないCamera#autoFocus()の使い方

Camera#autoFocus(AutoFocusCallback) | Android Developersが失敗すると
アプリが強制終了してしまうので気をつけて実装しなければいけません。
Camera#autoFocus() で失敗しないためのポイントを紹介していきましょう。

AndroidManifest.xml に記述を忘れない

基本中の基本です。
機種にオートフォーカス機能があるかどうかわかないので
android:required="false" を指定しています。
オートフォーカスが絶対に必要なアプリだったら android:required="false" にする必要はありません。
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />

hasSystemFeature でオートフォーカス機能をサポートしているか確認する

android:required="false" にしたらオートフォーカス機能をサポートしていない機種も
アプリをインストールできるので Camera#autoFocus() を呼び出す前には必ずチェックが必要です。
public static boolean hasFeatureAutoFocus(Context context){
    boolean hasAutoFocus = false;
    PackageManager packageManager = context.getApplicationContext().getPackageManager();
    hasAutoFocus = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS);
    return hasAutoFocus;
}

Camera#autoFocus() の連続呼び出し対策で Camera#cancelAutoFocus() を呼ぶ

Camera.AutoFocusCallback#onAutoFocus(boolean, Camera) | Android Developersが呼ばれる前に
次の Camera#autoFocus() が呼ばれると落ちるので事前にキャンセルする処理を入れます。
もはや上記のチェックも含めてラッパーメソッド作るのが一番楽です。
public void autoFocus(AutoFocusCallback autoFocusCallback){
    if(CameraUtils.hasFeatureAutoFocus(mContext)){
        mCamera.cancelAutoFocus();
        mCamera.autoFocus(autoFocusCallback);
    }
}

Camera#autoFocus() をカメラ起動直後に呼ばない

SurfaceHolder.Callback#surfaceCreated(SurfaceHolder) | Android Developersが呼ばれる前に
Camera#autoFocus() を呼ぶと落ちるので起動時にオートフォーカスをしないか、
状態をフラグ管理するか、try~catchで握りつぶすかのどれかになると思います。
DevCamera の Ver.0.0.2 では起動時の onResume で Camera#autoFocus() を呼んで
ARROWS X LTE F-05D で落ちるというバグがありました(※ Ver.0.0.3 で修正済み)
他の機種でも起こるかもしれませんが報告があったのは F-05D だけでした。
Camera#open() して Camera#autoFocus() する間に surfaceCreated が呼ばれないと
surfaceCreated が呼ばれる数ミリ秒前とかに Camera#autoFocus() が実行されて落ちます。
機種の処理速度によっては Camera#open() から surfaceCreated 呼ばれて
Camera#autoFocus() を呼び出すという流れになるので落ちずに済んでバグを見逃すという結果になります。
参考: Android Camera.autoFocus() failed | hitziger.net

また、Camera#autoFocus(AutoFocusCallback) | Android Developersにもあるとおり、
Camera#startPreview()Camera#stopPreview() の間でのみ有効らしいので
状態管理のフラグが必要かもしれません。

以上、失敗しない Camera#autoFocus() の使い方でした!

これで DevCamera の安定度が高まりました。ぜひ使ってみてください。
ダウンロードはこちら→ DevCamera - Google Play の Android アプリ

2013/08/22

[Android]ADT 22に更新してActionBarActivityを使ってみた

Eclipse ADT を ver.22 に更新して ActionBarSherlock の SherlockFragmentActivity から Android Support Library v7 の AppCompat の ActionBarActivity に乗り換えた時の話する

ハマりそうでハマらない少しハマる Android のお話です。
例によって、先人たちのお陰でそこまでハマらずにすんだので
僕も後人のために記録を残しておこうと思います。

アジェンダ

  1. ADT の ver.22 への更新
  2. Android SDK Manager の更新
  3. Android Support Library v7 の AppCompat の導入
  4. ActionBarSherlock から ActionBarActivity への乗り換え

1. ADT の ver.22 への更新

Eclipse の Help > Install New Software... で ADT を更新します。
特にハマることはありません。

2. Android SDK Manager の更新

ADT や Android SDK Manager の更新はたいてい何か起こるので
様子見しておいたおかげで少しハマる程度で済みました。
なにやら依存関係があるみたいで全部一括で更新できないと思ったら
1回更新後に Android SDK Build-tools が新たに現れるようです。
2回更新したら基本的には R.java が生成されてエラーは消えるようです。
Eclipse ADT 22 のバージョンアップでは Android SDK Manager のアップデートは 2 回する。

※ ActionBarSherlock を使っている場合はライブラリプロジェクトから削除しないように!
AndroidManifest.xml や res フォルダの ActionBarSherlock 由来のエラーを消すまで R.java が生成されなくて
事例報告のないハマりポイントで時間を費やすことになるので注意です。

おまけ
Android Private Libraries というものが追加されて、 Properties > Java Build Path > Order and Export で
Android Private Libraries にチェックがついていないとエラーになるようです。
(最近のバージョンは勝手にチェックがついているのでエラーにはならない?)
穀風: Eclipse ADT を Ver. 22 にしたら ClassNotFoundException が発生するようになった

3. Android Support Library v7 の AppCompat の導入

  • Eclipse の File > Import... で Existing Android Code Into Workspace を選択して
    <AndroidSDK>/extras/android/support/v7/appcompatをインポートして
    android-support-v7-appcompat というプロジェクトを追加する。
  • 自分のプロジェクトの Properties > Android > Library の Add から
    android-support-v7-appcompat をライブラリプロジェクトとして追加する。
参考
Support Library Setup | Android Developers

これで Android Support Library v7 の AppCompat の機能を使うことができます。
Support Library Features | Android Developers
Action Bar | Design Patterns | Android Developers
Action Bar | API Guide | Android Developers
ActionBar | Android Developers
ActionBarActivity | Android Developers
ShareActionProvider | Android Developers

4. ActionBarSherlock から ActionBarActivity への乗り換え

プロジェクトの Properties > Android > Library から ActionBarSherlock を削除します。
すると ActionBarSherlock に依存していた部分がエラーになるので修正しましょう。

Activity

-public class MainActivity extends SherlockFragmentActivity {
+public class MainActivity extends ActionBarActivity {
SherlockFragmentActivity を ActionBarActivity を変えるだけでほとんどエラーが無くなります。

Theme

<activity
    android:name="com.wada811.devcamera.MainActivity"
    android:icon="@drawable/ic_launcher"
    android:label="@string/appName"
-    android:theme="@style/Theme.Sherlock.Light.DarkActionBar" >
+    android:theme="@style/Theme.AppCompat.Light.DarkActionBar" >
</activity>
テーマは Sherlock や Holo を AppCompat に変えないと以下のような例外が発生します。
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

[Android]ActionBarSherlockの背景色をカスタマイズする | DevAchieveで作ったカスタムスタイルも
Sherlock を AppCompat に置き換える必要があります。
<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="ActionBarCustomStyle" parent="@style/Widget.Sherlock.Light.ActionBar.Solid">
+    <style name="ActionBarCustomStyle" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
         <item name="background">@color/actionBar</item>
         <item name="backgroundSplit">@color/actionBar</item>
         <item name="backgroundStacked">@color/actionBar</item>
         <item name="titleTextStyle">@style/ActionBarCustomTitleStyle</item>
     </style>
-    <style name="ActionBarCustomTitleStyle" parent="@style/TextAppearance.Sherlock.Widget.ActionBar.Title">
-        <item name="android:textColor">@color/abs__primary_text_holo_dark</item>
+    <style name="ActionBarCustomTitleStyle" parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
+        <item name="android:textColor">@color/white</item>
         <item name="android:textStyle">bold</item>
     </style>
</resources>
おまけ
Theme.Sherlock.Light.NoActionBar や Theme.Holo.Light.NoActionBar はあって、
Theme.AppCompat.Light.NoActionBar がないのはおかしいので自分で作った。
<style name="Theme.AppCompat.Light.NoActionBar" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

Menu

@Override
public boolean onCreateOptionsMenu(Menu menu){
-    getSupportMenuInflater().inflate(R.menu.activity_main, menu);
+    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}
呼び出し側のコードは MenuInflater を取得するメソッドが変わります。
また、Menu や MenuItem は Android のものを使うようになります。
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:support="http://schemas.android.com/apk/res-auto" >
    <item
        android:id="@+id/infoButton"
        android:icon="@drawable/ic_menu_info"
        android:title="@string/info"
-        android:showAsAction="ifRoom"/>
+        support:showAsAction="ifRoom"/>
</menu>
showAsAction 属性が API Level 11 から導入されたため、
それ以前は showAsAction 属性が存在しないので名前空間を定義して指定する必要があります。
上ではわかりやすさのため support としていますが、どんな名前でもいいです。
Using XML attributes from the support library | Action Bar | Android Developers
R.attr#showAsAction | Android Developers

ActionBarSherlock から ActionBarActivity への移行完了!

これでハマらずに移行することができるかと思います。

2013/08/20

iPhoneのSafariでフォントサイズが勝手に変わる際の対処法

iOSはおせっかいで余計なことを自動でやってくれちゃうので
スタイル指定を無視してフォントサイズを自動調整してくれちゃいます。

この自動調整を無効化する -webkit-text-size-adjust というプロパティがあるので
以下のように body などに記述しておくと良いと思います。

参考
iPhoneでフォントサイズが勝手に変わっちゃう時の解決方法 | YOUSORO.blog
-webkit-text-size-adjust: none を絶対に設定してはいけない理由 - てっく煮ブログ
body {
    -webkit-text-size-adjust: 100%;
}

2013/08/19

Rails4のMass Assignment脆弱性対策のStrong Parametersについて

Mac OSX10.8でrbenvを使ってRuby1.9とRails4.0をインストールする
ひょんなことから Rails4 をインストールしてしまった俺達は
attr_accessibleattr_protectedを使わずに
Strong Parameters を使うことになってしまっていた!
ActionController::StrongParameters
rails/strong_parameters

初めての Rails で右も左も分からない状況で
いきなり Strong Parameters と言われてもよくわからないので以下についてまとめます。

Mass Assignment とは

Rails ではこんなコードをよく見ると思います。
def create
  @user = User.new(params[:user])
  unless @user.save
    render 'new'    
  end
end
Userモデルにnameemailという属性が設定されていて、
params[:user]{name: 'willnet', email: 'willnet@example.com'}のような
パラメータが入っていたとき、上記のUser.new(params[:user])で一度にnameemail
設定されます。これが mass assignment です。属性が多いときなど大変便利です。
Rails4.0に含まれる strong_parameters について - willnet.in

Mass Assignment 脆弱性

先ほどの例ではnameemailという属性が設定されていました。
この2つの属性の他に、システム側で自動で割り振られるtokenという属性が存在したとします。
tokenはユーザには変更させたくありません。

さて、 それではUser#nameUser#emailを変更するための
update アクションを書いてみましょう。
edit の view には name と email を変更するための text_field だけが存在するとします。
def update
  @user = User.find(params[:id])
  unless @user.update_attributes(params[:user])    render 'edit'&
  end
end
上記のコードは基本的にはうまく動くはずです。ただしユーザが普通にブラウザを使う限りは。
  • chromeのデベロッパーツールなどで
    <input type="text" name="user[token]" value="hoge" />などのタグを
    form中に生成してsubmitする
  • curlやtelnetなどで直接user[token]=hogeのようなリクエストを、
    通常のリクエストに追加して送信する
上記のような方法を使った場合、ユーザに編集させたくないtoken属性が更新されてしまいます。
これが mass assignment の脆弱性と呼ばれるものです。
Rails4.0に含まれる strong_parameters について - willnet.in

Rails4 以前の Mass Assignment 脆弱性対策

attr_accessibleattr_protectedが提供されていました。
Rails2系(および3.0)だと後述のroleが設定できないため、
User#nameだけ更新したい場合とUser#nameとUser#email両方を更新させたい場合の
2種類があるケースなど、同じモデルに複数種類の mass assignment 脆弱性対策をかけたくなります。
Rails4.0に含まれる strong_parameters について - willnet.in
Rails 3.1以上からはattr_accessibleattr_protectedに role という機能が導入されました。
class User < ActiveRecord::Base
  attr_accessible :name
  attr_accessible :name, :email, as: :name_and_email
end
as オプションで role の名前を指定して、
@user.update_attributes(params[:user], role: 'name_and_email')
のように role オプションで適用したいattr_accessible(もしくはattr_protected)を指定します。
role がない場合はデフォルトのもの(上記のケースはattr_accessible :name)が使用されます。

これにより大分きちんと書けるようになったのですが、
多くの role を定義する必要があるとやや煩雑になります。
Rails4.0に含まれる strong_parameters について - willnet.in
role の設定が煩雑になった結果(?)、
GitHub、Mass Assignment利用の脆弱性を突かれるという事例が発生したので
この煩雑な role 設定の管理をしなくてはいけない状況をどうにかするべく
Strong Parameters が導入されたようです。

Rails4 の Mass Assignment 脆弱性対策の Strong Parameters について

rails generate scaffold user name:string email:string
scaffold を使用すると、Strong Parameters の機能を使用したコードが生成されます。
class UsersController < ApplicationController
  ...
  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)
    ...
  end
  ...
  private
  ...
    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :email)
    end
end
このコードは「params が :user というキーを持ち、params[:user] は :name 及び :email というキーを持つハッシュであること」を検証します。

例えば、params[:user]{:name => 'taro', :email => 'taro@example.com'}のようなハッシュである場合に検証 OK となります。
» Rails4 の Strong Parameters でリクエストパラメータを検証する TECHSCORE BLOG

許可していないパラメータが渡された場合はデフォルトでは無視される

試しに user_params の内容を以下のように修正してみます。
# Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      # 試に :email の指定を消す.
      # params.require(:user).permit(:name, :email)
      params.require(:user).permit(:name)
    end
この状態で再度ユーザ登録を行うと、リクエストパラメータとして名前(name)とメールアドレス(email)が渡されますが、メールアドレス(email)は無視されます。

ログを確認すると以下のようなメッセージが出力されていました。
Unpermitted parameters: email
» Rails4 の Strong Parameters でリクエストパラメータを検証する TECHSCORE BLOG

許可しないパラメータが渡された場合に例外を発生させる

許可しないパラメータが渡された場合の動作は config/application.rb で変更することができます。

試に例外を発生させるように変更してみます。
# デフォルトは :log で, 許可されていないパラメータは無視されたうえでログ出力されます.
config.action_controller.action_on_unpermitted_parameters = :raise
この状態で再びユーザ登録を行うと(リクエストパラメータに name と email が渡されると)、ActionController::UnpermittedParameters という例外が raise されました。
» Rails4 の Strong Parameters でリクエストパラメータを検証する TECHSCORE BLOG

ネストしたパラメータ

Strong Parameters のドキュメントを読むと、permit メソッドの引数を以下のように指定することで、
ネストしたパラメータに対応することができるようです。
params.permit(:name,
              {:emails => []},
              :friends => [:name,
                           {:family => [:name]}])
» Rails4 の Strong Parameters でリクエストパラメータを検証する TECHSCORE BLOG

全てのパラメータを許可する

開発中のときなど、全ての属性を許可したいケースがあると思いますが、
その時はparams[:user].permit!のようにします。
ただ、permit!が今のバージョン(0.1.4)だと再帰的に効かないという話しがあるので
(RailsCastsで言ってた。未確認)、うまくいかない場合は github 上の最新を使ってみてください。
Rails4.0に含まれる strong_parameters について - willnet.in

参考

Rails4.0に含まれる strong_parameters について - willnet.in
» Rails4 の Strong Parameters でリクエストパラメータを検証する TECHSCORE BLOG

参考というより引用元です。
どちらの記事も読み返したくなる記事内容で Chrome のタブを閉じることができなかったので
内容の9割以上が引用でしたが、記事にすることでタブを閉じることができそうです。
Rails やるときにまた見に行くと思います。
どちらもわかりやすい記事をありがとうございます。

2013/08/18

[Eclipse][Java]UTF-8で文字化けしないJavadocを生成する

「File > Export…」を選択し、「Java > Javadoc」を選択します。
Javadoc を生成したいプロジェクトを選択し、次へ進みます。

次の画面はよくわからないのでそのままで次へ進みます。

下の画像のように Javadoc 追加オプションに以下を追加します。
-encoding UTF-8
-charset UTF-8

Finish で 文字化けしていない Javadoc が生成されると思います。
参考
クラウドサービスプラットフォーム Cosminexus:EclipseからJavadocを生成するには?:ソフトウェア:日立
comscom@Wiki - Eclipse/Javadoc/Javadocの日本語が文字化けしちゃうの。

2013/08/17

[Android]ColorFilterで画像の色を変更する

僕の開発しているDevCameraでは
右のスクリーンショットのように
同じカメラアイコンの色違いを使っていますが、
これはそれぞれ画像を用意していません。
黒のカメラアイコンをAndroid Asset Studio
作成して ColorFilter で色を変えています。
ColorFilter を使えば
色違いの画像をイチイチ用意しなくても良く、
リソース容量も節約できるので開発者にもユーザーにもやさしいです。

ColorFilter を使う方法は3つあります。

Drawable に setColorFilter を指定する方法

Drawable#setColorFilter(int, PorterDuff.Mode) | Android Developers

ImageView に setColorFilter を指定する方法

ImageView#setColorFilter(int, PorterDuff.Mode) | Android Developers

ImageView の XMLタグに tint 属性を指定する方法

ImageView#attr_android:tint | Android Developers

どれも使い方は簡単なのでサンプルコードは省略します。

tint 属性では PorterDuff.Mode がPorterDuff.Mode#SRC_ATOPに固定されるので
@yanzmさんが PorterDuff.Mode を指定できるライブラリプロジェクトを公開しています。
Y.A.M の 雑記帳: Android ColorFilter を使う
色々な PorterDuff.Mode の指定結果のスクリーンショットもあるので一見の価値ありです。

2013/08/16

[HomeBrew]正規表現でファイル名を置換するrenameコマンド

Android Asset Studioでランチャーアイコンを生成するのだけど
ファイル構成が以下のようになっていて毎度ファイル名を修正するのが面倒だったので
rename コマンドをインストールしてみた。
rename // plasmasturm.org

res
├── drawable-hdpi
│   └── ic_launcher.png
├── drawable-mdpi
│   └── ic_launcher.png
├── drawable-xhdpi
│   └── ic_launcher.png
└── drawable-xxhdpi
    └── ic_launcher.png
brew install renameでやってみると何故か失敗するので調べてみると以下のようになっていたので
require 'formula'

class Rename < Formula
  url 'http://plasmasturm.org/code/rename/rename', :using => :nounzip
  version '0.1.3'
  homepage 'http://plasmasturm.org/code/rename'
  sha1 'a2235a402d18495513edf690445e0030f31c9ab3'

  def install
    system 'pod2man', 'rename', 'rename.1'
    bin.install 'rename'
    man1.install 'rename.1'
  end
end
以下のように修正したら成功しました。
require 'formula'

class Rename < Formula
  homepage 'http://plasmasturm.org/code/rename'
  url 'https://github.com/ap/rename/archive/v1.600.tar.gz'
  sha1 'a7946ce3602e3810aaa70300674ccb26832634ed'

  def install
    system 'pod2man', 'rename', 'rename.1'
    bin.install 'rename'
    man1.install 'rename.1'
  end
end
これで rename コマンドが使えるようになったけど再帰的にリネームはできなっぽいので
以下のようなコマンドで対応しました。
find res -type f -name 'ic_launcher.png' | xargs rename 's/launcher/orientation_landscape/'
res
├── drawable-hdpi
│   └── ic_orientation_landscape.png
├── drawable-mdpi
│   └── ic_orientation_landscape.png
├── drawable-xhdpi
│   └── ic_orientation_landscape.png
└── drawable-xxhdpi
    └── ic_orientation_landscape.png
正規表現使ってないけど rename コマンドが便利な時が来るはず。

きっかけ


これよりは便利。@gabuさんありがとうございます。

2013/08/15

[Android]ActionBarのListNavigationでドロップダウンを表示する

右のスクリーンショットは
僕の開発しているDevCameraのカメラ画面です。

撮影・連写・無音・無音連射ができるカメラが
分かれているので、カメラ選択画面に戻らずに
カメラを切り替えられるように
ActionBar の ListNavigation で表示しています。

DevCamera ではカメラアイコンの部分を
タップしたらカメラ選択画面へ戻り、
タイトル部分をタップしたらドロップダウンを表示するようにしています。
ActionBar#setDisplayHomeAsUpEnabled(boolean)
onOptionsItemSelected に id が android.R.id.home の MenuItem が
来るのでカメラ選択画面へ戻る処理を実装しました。
タイトル部分はドロップダウンにしたかったので
本来のタイトルは非表示にしています。(9, 10行目)

アイコンカラーの設定はわかりにくいですが、
継承でアイコンとカラーを変えています。

ListNavigation の設定は ActionBar#setNavigationMode(int)
ActionBar#NAVIGATION_MODE_LIST を設定し、
ActionBar#setListNavigationCallbacks(SpinnerAdapter, ActionBar.OnNavigationListener)
後述の表示部分とドロップダウンリストがタップされた際のリスナーを登録します。
ActionBar#setSelectedNavigationItem(int) | Android Developersで選択状態を設定できます。
public class BaseCameraActivity extends SherlockFragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        // ActionBar の設定
        mActionBar = getSupportActionBar();
        mActionBar.setDisplayHomeAsUpEnabled(true);
        mActionBar.setDisplayShowTitleEnabled(false);
        // アイコンのカラー設定
        CameraItem camera = App.sCameras.get(getCameraId());
        BitmapDrawable drawable = (BitmapDrawable)getResources().getDrawable(camera.getIconId());
        drawable.setColorFilter(getResources().getColor(camera.getColorId()), Mode.SRC_ATOP);
        mActionBar.setIcon(drawable);
        drawable.setCallback(null);
        // ListNavigation の設定
        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        mActionBar.setListNavigationCallbacks(new CameraSpinnerAdapter(this), new OnNavigationListener(){
            @Override
            public boolean onNavigationItemSelected(int itemPosition, long itemId){
                if(itemPosition != getCameraId()){
                    // カメラ切り替え
                    startActivity(new Intent(getApplicationContext(), App.sCameras.get(itemPosition).getClazz()));
                    finish();
                    return true;
                }
                return false;

            }
        });
        mActionBar.setSelectedNavigationItem(getCameraId());
    }

}
タイトル部分の表示が getView で、ドロップダウン部分の表示が getDropDownView で設定できます。
[Android]ListViewのitem毎に有効/無効を設定する | DevAchieve
無効化、グレーアウトを解説しているのでサンプルでは省略しています。
public class CameraSpinnerAdapter extends ArrayAdapter<CameraItem> implements SpinnerAdapter {

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view;
        if(convertView == null){
            view = mLayoutInflater.inflate(R.layout.camera_spinner_item, parent, false);
        }else{
            view = convertView;
        }
        CameraItem item = getItem(position);
        ((TextView)view.findViewById(R.id.title)).setText(item.getTitleId());
        return view;
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent){
        View view;
        if(convertView == null){
            view = mLayoutInflater.inflate(R.layout.camera_spinner_dropdown_item, parent, false);
        }else{
            view = convertView;
        }
        CameraItem item = getItem(position);
        ImageView icon = (ImageView)view.findViewById(R.id.icon);
        icon.setImageResource(item.getIconId());
        icon.setColorFilter(getContext().getColor(item.getColorId())));
        TextView title = (TextView)view.findViewById(R.id.title);
        title.setText(item.getTitleId());
        title.setTypeface(Typeface.DEFAULT_BOLD);
        return view;
    }

}

2013/08/14

[Android]ListViewのitem毎に有効/無効を設定する

あまり需要はないかと思いますが、
ListView の item 毎に有効/無効を
設定する方法を知ったので
書いておこうと思います。

右のスクリーンショットは僕の開発した
DevCamera のカメラ選択画面です。
複数のカメラを使用することができますが、
初期状態では通常カメラのみを使用できるようにしたくなかったので
それ以外は無効化(グレーアウト)しています。

このように item 毎に有効/無効を設定するには
BaseAdapter の isEnabled メソッドをオーバーライドして
item 毎に true/false を返してやれば良いです。
BaseAdapter#isEnabled(int) | Android Developers

DevCamera では CameraItem クラスのコンストラクタで
enabled を設定して、下のハイライトしている行で返しています。

下のサンプルは ListView の item 毎に有効/無効を設定する部分と
無効化した item のグレーアウトの処理の部分以外を
適当に省略したコードです。
public class CameraItem {

    int      colorId;
    boolean  enabled;

    public int getColorId(){
        return enabled ? colorId : R.color.darkGray;
    }

    public boolean isEnabled(){
        return enabled;
    }

}

public class CameraListAdapter extends ArrayAdapter<CameraItem> {

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view;
        if(convertView == null){
            view = mLayoutInflater.inflate(R.layout.fragment_camera_list_row, parent, false);
        }else{
            view = convertView;
        }
        CameraItem item = getItem(position);
        ImageView icon = (ImageView)view.findViewById(R.id.icon);
        icon.setImageResource(item.getIconId());
        icon.setColorFilter(getContext().getResources().getColor(item.getColorId())));
        TextView title = (TextView)view.findViewById(R.id.title);
        title.setText(item.getTitleId());
        if(item.isEnabled()){
            title.setTypeface(Typeface.DEFAULT_BOLD);
        }else{
            title.setTextColor(getContext().getResources().getColor(R.color.darkGray));
        }
        return view;
    }

    @Override
    public boolean isEnabled(int position){
        return getItem(position).isEnabled();
    }

}

2013/08/06

[Webサービス]RSSサービスfeedlyの永久会員に99ドルでなってみた

feedlyに有料版が登場!いまなら先着5000名に99ドル永久会員 | ガジェット速報
月額5ドル(約490円)または年額45ドル(約4430円)の2種類で、
HTTPS通信のサポートをはじめとした有料サービスが受けられます。
なお、先着5,000名には99ドル(約9750円)で
永久にPro版が使い放題のキャンペーンも実施中です。
有料版で解放される機能は、以下の通りです。
  • HTTPS通信のサポート
  • 自分のフィードリスト内における記事検索機能のサポート
  • Evernote連携
  • プレミアムサポート
有料版で記事検索機能サポート来たー!
気になった記事見つけたらツイートしてふぁぼれば IFTTT で Tumblr に投稿されて
後から読みたくなったら Tumblr の RSS を Google Reader で全文検索すれば良いという最強コンボが
feedly でもできるようになるので課金せずにはいられない!
Google Reader のように金にならないからって死なれたら困るし。
全文検索も特に問題なく動きそうな感じ。さすがに feedly 使う前のフィードについては検索できないみたい。

feedly は Android アプリにコントリビュートさせてほしい。画面離れたら毎回リロードするのどうにかして。