2015/01/26

[Android] Support Library に追加された testing-support-lib や Espresso 2.0 で JUnit4 な Android のテストを書く

Android Support Library に UI テストを行う Espresso 2.0 や
JUnit4 でテストを行うための testing-support-lib が追加されました。

前置き

概要とドキュメントなど

Espresso 2.0 is here!

android-test-kit - Google's Testing Tools For Android - Google Project Hosting


サンプルコード

googlesamples/android-testing


関連エントリ

Espresso 2.0 が Android support library の一部としてリリースされた - ひだまりソケットは壊れない
Espressoがsupport libraryになってAndroidでJUnit4が使えるようになったと聞いたので試してみた - みんからきりまで
続・AndroidでJUnit4を使う方法(Android SDKで正式サポートされました!) - Qiita
support packageに追加されたtesting-support-libを使ってAndroidのテストをJUnit4で書く - visible true


JUnit4 で Activity のテストを書いてみる

@@ -10,6 +10,8 @@ android {
         targetSdkVersion 21
         versionCode 1
         versionName "1.0"
+        // testing
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
         release {
@@ -17,9 +19,16 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    packagingOptions {
+        exclude 'LICENSE.txt'
+    }
 }

 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile 'com.android.support:appcompat-v7:21.0.3'
+    // testing
+    compile 'com.android.support:support-annotations:21.0.3'
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
 }
package com.wada811.android.junit4;

import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest extends ActivityInstrumentationTestCase2 {

    private MainActivity activity;

    public MainActivityTest(){
        super(MainActivity.class);
    }

    @Before
    public void setUp() throws Exception{
        super.setUp();
        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
        activity = getActivity();
    }

    @After
    public void tearDown() throws Exception{
        super.tearDown();
    }

    @Test
    public void checkPrecondition(){
        assertThat(activity, notNullValue());
        assertThat(getInstrumentation(), notNullValue());
    }

}

参考

Getting Started - EspressoSetupInstructions - android-test-kit
AndroidJUnitRunnerUserGuide - android-test-kit

テストを実行する

./gradlew connectedAndroidTest コマンドを実行

端末もしくはエミュレータでテストを実行することができます。
しかし、何故か MainActivityTest.java は実行されませんでした。
JUnit3 なテストは実行されていますが、 JUnit4 な テストは実行されないみたいです。

プロジェクトエクスプローラで右クリックから Run...

ファイルを指定して実行すれば JUnit4 なテストも実行できます。


Edit Configurations... から Android Test を追加してクラスを指定して実行

クラス(ファイル)を指定して実行すれば JUnit4 なテストも実行できます。
All in Module や All in Package では JUnit4 なテストは実行されませんでした。


クラスを指定しなくても JUnit4 なテストを実行したい

これは偶然なのか考えられているのかわからないけど Jake 神の ActivityRule を使うと
直接クラスを指定しなくてもテストを実行できるようになります。
package com.wada811.android.junit4;

import android.support.test.runner.AndroidJUnit4;
import com.jakewharton.test.ActivityRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.*;

@RunWith(AndroidJUnit4.class)
public class MainActivityTestWithRule {

    @Rule
    public final ActivityRule activityRule = new ActivityRule<>(MainActivity.class);

    @Test
    public void checkPrecondition(){
        // MatcherAssert.assertThat(activityRule.instrumentation(), Matchers.notNullValue());
        assertThat(activityRule.instrumentation(), notNullValue());
    }

    @Test
    public void showHelloWorld(){
        // Espresso.onView(ViewMatchers.withId(R.id.textView)).check(ViewAssertions.matches(ViewMatchers.withText(R.string.hello_world)));
        onView(withId(R.id.textView)).check(matches(withText(R.string.hello_world)));
    }

}
Espresso 2.0 でテストを書いてみました。
クラス名を覚えるのは辛い感じなので static import でメソッド書くだけにしたい感じがあります。

ソースコードは wada811/Android-JUnit4 にあります。
Activity だけじゃなく他のテストについても増やしていきたいです。

参考

A JUnit @Rule which launches an activity when your test starts. Stop extending gross ActivityInstrumentationBarfCase2!
2015/01/08

2014年の反省と2015年の目標

あけましておめでとうございます。
2012/01/08に始まったこのブログも
3周年を迎えました。
2013年は117記事でしたが、
2014年は72記事となって減少傾向です。
細かい Tips などは Qiita に
書くようになったので
wada811(Qiita) もよろしくお願いします。
昔からの人も最近からの人も
2014年、お世話になりました!
2014年のエンジニア人生を振り返って
2015年の目標を立ててみようと思います。

2014年

結婚した

撮った写真を披露宴会場のスクリーンに表示するシステムが3万円くらい、と
結婚式の2週間前に TV でやっているのを見て
こんなのすぐできる!1人日程度だろ?と思って写真アップロードシステム作ったのは良い思い出でした。
実際は細切れにしか作業できなかったので総日数としては3日くらいかかりましたが。
借りてたさくらのVPSにPHPをインストールしてなかったとか
ドメインを取ってなかったので契約からスタートとか色々ありましたが、
開発だけなら1人日程度でできたと思います。
wada811/wedding.wada811.com
とにかく時間がなくて画面も3枚だけだったので手っ取り早く素の PHP で作りました。
脆弱性とかはないと信じたい。。。
開発で一番時間がかかったのは PDO の薄いラッパーを探すことで、
プリペアドステートメントでクエリ発行できるやつが見つからなかったので仕方ないから自分で作りました。
あとは、CSS3 の transform をアニメーションの調整くらい。
関連記事
さくらのVPSにスタードメインで取得した独自ドメインを設定してみた
jquery.transform.js で CSS3 の transform をアニメーションさせる
exif-js と ios-imagefile-megapixel でスマホからの画像アップロードのプレビュー表示に対応する

Androider としての技術力を更に高める

どちらかというとライブラリ作ってた感じ。
wada811/Android-DialogFragments
wada811/Android-Material-Design-Colors
カメラのライブラリも作ってほぼほぼできていたけど放置していて完成したのかどうかも忘れている現状…。
カメラのライブラリは設計がいいのかどうかもわからなくて、
他の人がカメラアプリ作るときどんな感じに作っているのか知りたい。
The Android Arsenal - LandscapeVideoCameraとか、
そこの See Also とか見ればわかるかな。

新しい言語を学ぶ

Ruby on Rails 始めました。
Qiitaで Ruby とか Rails の知見を共有しています。
View は Slim で書いていて、もう PHP で、HTML で、というのには戻れない感じです。
そういう感じなのでブログも生の HTML を書くのが面倒になってきて Markdown で書きたいなぁという感じです。

OpenGL を学ぶ

やりませんでした。たぶん来年もやりませんね。

UnitTest / CI を学ぶ

Rails で Rspec を使ってテスト書いたり、 Guard でファイル変更を監視して自動的に Rspec を実行とかやった。
Rails はとにかくすぐ壊れる印象なのでテストがないとリファクタリングできないという感覚になっている。
テストを書く習慣が定着してるのはそういうことか〜という感じです。

個人でのプロダクトを出す

ライブラリをプロダクトと呼ぶかは微妙だけど、それ以外で言うと何もないなぁという感じです。
ひたすら GitHub Star を集めたり Qiita Stock を集めたりしてモチベーションを保っていた。

収入を増やす

一応増えたけどコスパ良くない。税金が重い。社会は厳しいという感じ。

2015年

もっと Android

あんまり Android で DB を使ったアプリを作ってないので作りたい。
RxJava, RxAndroid, Lombok あたりを使ってモダンな感じで作りたい。
Material Design に対応したい。
この3本で頑張っていこうと思います。

もっと Ruby on Rails

まだ1ヶ月やっただけなので、もっと経験を積みたいです。
アプリのバックエンドで使ってみる。

もっと UnitTest / CI

コミットしたらテスト実行されて…という環境を作る。

個人でのプロダクトを出す

新しいものを考えてはある。あとは作るだけ。

収入を増やす

コスパあげたい。

ブログを移行する

GitHub Pages に移行して Markdown で記事書きたい。
でも GitHub Pages をやめちゃった人がいて、面倒なのかとか気になっているところ。
ブログのカスタマイズにはあまり時間を掛けたくないので悩んでいる。

2015年もよろしくお願いします!

さらなる飛躍の年にするべく頑張ります!
2014/12/30

[Android]テキストをクリップボードにコピーする

wada811/Android-Material-Design-Colors のサンプルアプリを作ったら
カラーコードをコピーしたいという要望があったので実装しました。

Honeycomb 未満と以上で方法が変わっていました。
クラス名は同じでパッケージが異なるだけだったので長ったらしくてちょっとアレです。

@SuppressWarnings("deprecation")
public void copyText(Context context, String text){
    if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB){
        android.text.ClipboardManager clipboard = (android.text.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        clipboard.setText(text);
    }else{
        android.content.ClipboardManager clipboard = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied Text", text);
        clipboard.setPrimaryClip(clip);
    }
}
Android: Copy to clipboard selected text from a TextView - Stack Overflow

[Android]タブレット対応 〜 Master / Detail パターンを実装する

みなさん、タブレット対応してますか?
僕はまだあんまりなので
とりあえず一番有名な Master / Detail パターンを実装してみることにしました。
Android Studio の Wizard に Master Detail パターンがあるので
コイツを見ておきたいと思います。

ということで Android Studio の Wizard で Master Detail パターンを生成してみると
結構わかりにくいことをしていて、
values-sw600dp/refs.xml で @layout/activity_item_list を参照すると
タブレットでは @layout/activity_item_twopane を参照されるように設定しています。
<item type="layout" name="activity_item_list">@layout/activity_item_twopane</item>
しかも、スマートフォンとタブレットの判別の仕方が微妙で
ItemListActivity.java で setContentView(R.layout.activity_item_list) すると
タブレットでは R.layout.activity_item_twopane が読み込まれて
そのレイアウトの中に R.id.item_detail_container な View があるかで
スマートフォンとタブレットのレイアウトを振り分けています。
setContentView(R.layout.activity_item_list);

if(findViewById(R.id.item_detail_container) != null){
    mTwoPane = true;
}

タブレットかどうかの判定と View の実装は分離しておきたいので
タブレットかどうかのフラグをリソースに定義することにします。
Yukiの枝折: Android:タブレットorスマートフォンのフラグをリソースに定義する
<resources>
    <bool name="isTablet">false</bool>
</resources>
<resources>
    <bool name="isTablet">true</bool>
</resources>

ItemListActivity.java ではこのフラグを参照して処理をわけます。
レイアウトもこのフラグで振り分けます。

public class ItemListActivity extends ActionBarActivity implements ItemListFragment.Callbacks{

    private boolean isTablet;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        isTablet = getResources().getBoolean(R.bool.isTablet);
        setContentView(isTablet ? R.layout.activity_item_twopane : R.layout.activity_item_list);

        if(isTablet){
            getSupportActionBar().setDisplayShowHomeEnabled(false);
            getSupportActionBar().setDisplayUseLogoEnabled(false);

            ItemListFragment itemListFragment = (ItemListFragment)getSupportFragmentManager().findFragmentById(R.id.item_list);
            itemListFragment.setActivateOnItemClick(true);
            onItemSelected(0);
        }
    }

    @Override
    public void onItemSelected(int position){
        if(isTablet){
            Bundle arguments = new Bundle();
            arguments.putInt(ItemDetailFragment.ARG_ITEM_ID, position);
            ItemDetailFragment fragment = new ItemDetailFragment();
            fragment.setArguments(arguments);
            getSupportFragmentManager().beginTransaction()
                .replace(R.id.item_detail_container, fragment)
                .commit();
        }else{
            Intent detailIntent = new Intent(this, ItemDetailActivity.class);
            detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, position);
            startActivity(detailIntent);
        }
    }
}
wada811/Android-Material-Design-Colors の サンプルアプリ は
Android Studio の Wizard の Master Detail パターンを元に
上記の変更を加えたアプリになっているので参考になるかもしれません。
ソースコードはこちら↓
Android-Material-Design-Colors/sample at master · wada811/Android-Material-Design-Colors

2014年に @wada811 が読んだ技術書まとめ

2014年も終わりに近づいているので@wada811が読んだ技術書を振り返ってみようかと思います。
2013年はこちら→2013年に @wada811 が読んだ技術書まとめ | DevAchieve(11冊)
2012年はこちら→2012年に @wada811 が読んだ技術書まとめ | DevAchieve(約40冊)

2014/05/07 [書評]ノンデザイナーズ・デザインブック | DevAchieve

色々な所で読んでおくべきデザイン本として紹介されている本です。
4つの基本原則も良いけど、個人的にはジョシュア・ツリーの悟りの話が好きです。
一度名前を呼ぶことができれば、
あなたはそれを意識し、それを支配し、それを所有し、
それをコントロールできるようになる。
名前大事ですね。

1年で書籍1冊の書評を書いた!

全然読んでないけど大丈夫か、という感じだ…。
Linuxエンジニア養成読本は職場にあったので読んだけど
【改訂新版】Linuxエンジニア養成読本が出ていた。
他の事に時間使っていて本読んでない…。

2014年に @wada811 が積んだ技術書まとめ

増補改訂版Java言語で学ぶデザインパターン入門
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
JUnit実践入門 体系的に学ぶユニットテストの技法
レガシーコード改善ガイド
Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

重い本ばっかり積んでる…。
手を動かしつつ、ちゃんと本も読んでいきたい。
2014/12/09

Scala + sbt + Sublime Text で 始める Play2

インストール

Homebrew で scala, sbt, play をインストール

$ brew install scala
$ brew install sbt
$ brew install play

$ scala -version
Scala code runner version 2.11.1 -- Copyright 2002-2013, LAMP/EPFL
$ sbt --version
sbt launcher version 0.13.5
$ play
[info] Loading global plugins from /Users/ssl001/.sbt/0.13/plugins
[info] Loading project definition from /Users/ssl001/Documents/workspace_play/hello-play/project
[info] Set current project to hello-play (in build file:/Users/ssl001/Documents/workspace_play/hello-play/)
           _
     _ __ | | __ _ _  _
    | '_ \| |/ _' | || |
    |  __/|_|\____|\__ /
    |_|            |__/

play 2.2.3 built with Scala 2.10.3 (running Java 1.6.0_65), http://www.playframework.com

Sublime Text 3 で sublimescala/sublime-ensime をインストール

Cmd+Shift+P -> Package Control: Install Package

Ensime

ENSIME Server (ensime/ensime-src)をインストール

Download releases

$ cd ~/Library/Application Support/Sublime Text 3/Packages/Ensime
$ mkdir Server
$ mv ~/Downloads/ensime_2.10.0-0.9.8.9/* Server

sbt に Ensime プラグイン(ensime/ensime-sbt-cmd)を追加

~/.sbt/0.13/plugins/plugin.sbt に以下を追加(ファイルがなければ作成)

addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "0.1.2")

sbtコマンドによる.ensimeファイルの作成

$ play new hello-play
$ cd hello-play
$ sbt
> ensime generate

以降、Sublime Text で Cmd+Shift+P -> Ensime: startup で補完が効くようになる


Sublime Text 3 で guillaumebort/play2-sublimetext2 をインストール

Cmd+Shift+P -> Package Control: Install Package

Play 2.0


Sublime Text 3 で jarhart/SublimeSBT をインストール

Cmd+Shift+P -> Package Control: Install Package

SublimeSBT


実行

Cmd+Shift+P -> SBT: Run


参考

最強!?Ensimeを利用したSublime Text3のScala開発環境(playでもliftでも) - Qiita
PANAXIOM — Sublime Text 2 with SBT and Play 2


感想

sublime-ensime と SublimeSBT の開発が止まっていてな…
快適とは言い難いので他の IDE を使った方が良いと思うんだ。

Ruby on Rails 開発環境構築 in Mac OS X Mavericks

久しぶりに Ruby on Rails で開発することになり、開発環境を再構築しました。
関連: Mac OSX10.8でrbenvを使ってRuby1.9とRails4.0をインストールする

HomeBrew を使用するので MacPorts の人は読み替えてください。
また、僕は zsh を使っているので bash の人も読み替えてください。
HomeBrew や zsh をインストールしようという人は以下の事が参考になるかと思います。
Homebrew + Homebrew-caskで Mac の開発環境を構築する | DevAchieve
MacにHomeBrewとzshを導入してみた | DevAchieve

rbenv と ruby-build のインストール

brew install rbenv
brew install ruby-build
# rbenv の初期化
eval "$(rbenv init -)"
source .zshrc
eval "$(rbenv init -)" で rbenv へのパスを通すのとコマンドの補完や引数の処理、rehash をしています。
echo "$(rbenv init -)" で処理内容を確認できます。

rehashとは、
rbenv -h で確認すると以下のように書かれています。
rehash: Rehash rbenv shims (run this after installing binaries)
gem とか入れたらやれってことでいいのだと思います。
gem を入れまくると重くなる?のか rehash が重くなることがあるらしいです。
猫好きモバイルアプリケーション開発者記録 rbenv init が重い問題に対する対処法
gem を(アン)インストールしたら自動で rehash してくれる gem があるのでこれを入れておくと良いかも。
ryansouza/rbenv-rehash - github

依存パッケージのインストール

brew install readline
brew link readline
brew install openssl
brew link openssl

Ruby をインストールする

CONFIGURE_OPTS="--with-readline-dir=/usr/local --with-openssl-dir=/usr/local" rbenv install 2.1.5
CONFIGURE_OPTS="--with-readline-dir=/usr/local --with-openssl-dir=/usr/local" rbenv install まで
入力して Tab で補完すればバージョンを指定できます。

rehash して使用するバージョンを指定する

rbenv rehash
rbenv global 2.1.5

バージョンを確認する

rbenv version
2.1.5 (set by /Users/wada/.rbenv/version)
ruby -v
ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]

gem 管理システム bundler のインストール

Ruby のライブラリである gem をパッケージ管理できる Bundler をインストールします。
gem install bundler
基本的にシステムにgemをインストールせず、
bundler でプロジェクトごとに gem をインストールするのが環境をクリーンに保てて良いとされています。
Rails も bundler でインストールしたいと思います。

Rails をインストールする

% mkdir project
% cd project
% bundle init
Writing new Gemfile to /path/to/project/Gemfile
Rails プロジェクト作成する際にbundle initして Gemfile を作成します。
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
4行目のコメントアウトを外して以下のコマンドを実行すれば rails をインストールできます。
bundle install --path vendor/bundle
他の gem を追加するときも上のコマンドでプロジェクト内にインストールします。

bundle exec rails new .
bundler でインストールした gem は
プロジェクトのディレクトリで bundle exec を付けてコマンドを実行する必要があります。

参考
Ruby - システムのgemにrailsをインストールせずrails newする - Qiita

libiconv is missing. で gem install nokogiri が失敗する場合

Homebrew で libiconv とその依存パッケージをインストールします。
### For nokogiri
brew tap homebrew/dupes
brew install libiconv
brew install libxml2
brew install libxslt
brew link --force libxml2
brew link --force libxslt

nokogiri をインストールします。
gem install nokogiri -- --use-system-libraries
Bundler を使用している場合は以下のコマンドを実行します。
bundle config build.nokogiri --use-system-libraries
bundle install

参考
Ruby - bundle install でnokogiriをインストールしろと言われる - Qiita
OSX - Mac OS X Mavericksで”gem i nokogiri”失敗の解決方法 - Qiita
2014/12/05

Homebrew + Homebrew-caskで Mac の開発環境を構築する

Brewfile + Homebrew + Homebrew-caskで Mac の環境構築をするの更新版です。

パッケージ管理システム「Homebrew」とその拡張版「Homebrew Cask」を使って
Mac の開発環境を構築します。
Brewfile と brew bundle コマンドはサポートされなくなったので
brew-update コマンドを作成して、brew-update でインストール/アップデートを行うようにします。

Homebrew を導入する

Command Line Tools for Xcode をインストールする

Xcode をインストールする
まずは AppStore から Xcode をインストールして下さい。
Command Line Tools for Xcode をインストールする
[ Xcode > Open Developer Tool > More Developer Tools... ] をクリックすると
Downloads for Apple Developer ページが開くので
Command Line Tools for Xcode をダウンロードしてインストールします。
参考: MavericksでCommand Line Tools for Xcodeをインストールする - Qiita

Homebrew をインストールする

Terminal で以下のコマンドを実行します。
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
参考: Homebrew — The missing package manager for OS X
Homebrew が正常にインストールできたか確認する
以下のコマンドを実行してエラーがあれば対処、再度実行します。
brew doctor

Homebrew-cask を導入する

Homebrew-cask をフォーミュラに追加する

Formula(フォーミュラ)は Homebrew で管理されているパッケージのことです。
Homebrew-cask は Homebrew では管理されていないので自分で追加する必要があります。
以下のコマンドで Homebrew-cask を Formula に追加することができます。
brew tap caskroom/cask
参考: これは便利!Homebrewに追加されたtapコマンドはリポジトリを追加して簡単にフォーミュラを増やせる | Macとかの雑記帳

Homebrew-cask をインストールする

Homebrew-cask を追加したら Homebrew の以下のコマンドでインストールします。
brew install brew-cask

おまけ: Homebrew-cask の使い方

アプリケーションを検索する
brew cask search chrome
google-chrome
アプリケーションをインストールする
brew cask install google-chrome
アプリケーションの実体は /opt/homebrew-cask/Caskroom に入り、
~/Applications にシンボリックリンクが作成されます。
それぞれのパスは以下のコマンドオプションで変更することができます。
--caskroom=/my/path, --appdir=/my/path
参考: homebrew-caskが良さそうなので導入してみた - About Digital
アプリケーションをアンインストールする
brew cask uninstall google-chrome
アプリケーションを更新する
brew cask update
古いアプリケーションを削除する
brew cask cleanup
その他の使い方
homebrew-cask/USAGE.md at master · caskroom/homebrew-cask

brew-update コマンドを作成して Homebrew パッケージを管理する

Brewfile を作成する

以下のようなファイルを作成します。
Install Packages と Install Applications の欄は自由にインストールしたいものを記述して下さい。
#!

# Update Homebrew
brew update

# Upgrade Formulas
brew upgrade

# Install HomebrewCask
brew tap caskroom/cask
brew install brew-cask

####################
# Install Packages #
####################

brew install zsh

########################
# Install Applications #
########################

brew cask install google-chrome

# Remove outdated versions
brew cleanup
brew cask cleanup
参考までに僕の brew-update にリンクをはっておきます。
dotfiles/brew-update at master · wada811/dotfiles

brew-update でインストール/アップデートする

パスの通った場所に brew-update を置いて、実行します。
brew bundle

さいごに

これで brew-update コマンドで Homebrew パッケージをインストール/アップデートできるようになりました。
2014/11/28

exif-js と ios-imagefile-megapixel でスマホからの画像アップロードのプレビュー表示に対応する

画像アップロードするときにプレビュー欲しいよね〜
プレビューするとたまに画像が回転されてるよね〜
スマホだと画像選択するアプリで EXIF 情報を見て回転させて表示しているから
プレビューが横向きとかになってておかしいと思われがちだよね〜

そんな問題を解決するのが exif-js です。

exif-js

jseidelin/exif-js

使い方は https://github.com/jseidelin/exif-js/blob/master/example/index.html を見てください。
例えば、アップロードされたファイルに対して Orientation を取得する場合は以下のように記述します。
$('#jsFileInput').on("change",function(){
    var file = $(this).prop("files")[0];
    EXIF.getData(file, function(){
        var orientation = file.exifdata.Orientation;
    });
});
EXIF 情報を取得するメソッドが提供されていて、
EXIF.getTag(file, "Orientation") で Orientation を取得できるようだけど
取得できないこともあるみたい?で file.exifdata.Orientation で直接取得してしまっています。
よくわからないので動作確認してもらえればと思います。

さて Orientation は取れましたが、どうやって画像を回転しましょう。
そんな時は megapix-image.js が便利でした。

megapix-image.js

stomita/ios-imagefile-megapixel

iOS とか入っていますが気にしなくても大丈夫です。
megapix-image.js は大きな画像を表示する際に便利なライブラリですが、
回転もやってくれるので表示は全部任せてしまいましょう。
スマホからだと数 MB 〜 十数 MB の画像が平気でアップロードされるので
普通にプレビューさせようとするとブラウザごとメモリ不足で落ちることがあるのでが
megapix-image.js を使えば、そういったことが少なくなります。
(※直接は見たことないが、画面が真っ暗になるなどの報告を受けたことがあるので銀の弾丸ではない?)

さきほどの exif-js と組み合わせて使うには以下のようにします。
$('#jsFileInput').on("change",function(){
    var file = $(this).prop("files")[0];
    EXIF.getData(file, function(){
        var orientation = file.exifdata.Orientation;
        var mpImg = new MegaPixImage(file);
        mpImg.render($("#jsPreviewImage")[0], { orientation: orientation });
    });
});

まとめ

これで回転している大きな画像をプレビュー表示することができるようになったと思います。
まれに表示できないこともあるらしいので、もっと良い方法があれば教えて下さい。
2014/11/27

jquery.transform.js で CSS3 の transform をアニメーションさせる

要素を CSS3 の transform で移動・回転・拡大縮小などさせつつ、
アニメーションもさせたい場合に便利な jQuery プラグインを紹介します。

jquery.transform.js

eenox/jQuery-3D-transform

このプラグインは指定の面倒な transform 属性を簡単に指定できるようにします。
transform 属性については transform-CSS3リファレンス を参照して下さい。
transform 属性は各種変換関数をスペース区切りで指定しなくてはならないので面倒でしたが、
普通の CSS の属性を扱うように transform 属性の関数を扱うことができます。
$(".transform").css({
    "top": top1,
    "left": left1,
    "opacity": "0",
    "rotate": rotate1 + "deg",
    "scale": "2, 2"
  }).animate({
    "top": top2,
    "left": left2,
    "opacity": "1",
    "rotate": rotate2 + "deg",
    "scale": "1, 1"
  }, 2000, 'swing');

transform 属性を上記のように簡単に扱えるというだけで
アニメーションさせなくても使いたい jQuery プラグインです。

参考

jQueryアニメーションでCSS3のTransformを操作して拡大縮小/回転などを実装させるいろいろ | BlackFlag
2014/11/19

さくらのVPSにスタードメインで取得した独自ドメインを設定してみた

さくらののVPSにスタードメインで取得した独自ドメインの設定してみました。
個人でVPSを借りて遊ぶのには hosts ファイル書き換えで十分でしたが、
サイトを他の人にも見せることになったので
IP でアクセスしてもらうのもカッコがつかないなぁ、ということでドメインを取得しました。
安くて Twitter などでも評判が良く、Whois情報公開代行も無料で行ってくれるスタードメインにしました。

さくらのVPS

コントロールパネルにログイン

さくらのVPSコントロールパネルにログインし、左メニューにある「ネームサーバ登録」をクリックします。

「さくらインターネットの会員メニュー」ログインページに移動します。

「会員ID」「パスワード」を入力し、「ログイン(認証)」をクリックします。

新しいネームサーバの登録

ページの下の方に「新しいネームサーバの登録」があります。

設定する「ドメイン名」を入力し、「送信する」をクリックします。

ゾーンの追加が実施されるので、ゾーンの初期設定をするために「ドメインメニューへ」をクリックします。

ドメイン名の一覧が表示されるので、設定するドメインの「ゾーン編集」をクリックします。

簡単設定の項目の「サーバのIPアドレス」にVPSのIPアドレスを入力し、「送信する」をクリックします。

さくらインターネットのネームサーバ

設定したドメインの情報が表示されます。

先ほど入力したIPアドレスは、「Aレコード」に追加されています。
ネームサーバーの情報(タイプが NS のもの)は、
スタードメイン側に登録するのでページは閉じないでおきましょう。

スタードメイン

「さくらのネームサーバ」を利用するように設定したので、
スタードメイン側から「さくらのネームサーバ」を参照するように設定します。

スタードメインを運営している NetOwl の管理ページから
「スタードメイン管理」の「管理ドメイン一覧」を開いて「ドメイン管理ツール」をクリックします。

「ネームサーバの確認・変更」をクリックします。

ネームサーバにさくらのVPSのネームサーバーの情報を入力して保存します。

ドメイン設定完了

数時間くらいすれば名前解決されてドメインでアクセスすれば
さくらのVPS側にアクセスできるようになります。

DNS逆引きレコード変更

DNS逆引きは IP アドレスからドメイン名を取得することで、
DNS逆引きレコードが設定してあれば取得できる。
DNS逆引きレコードが設定してあると
どこの馬の骨かもわからないサーバではない(※自称)と宣言できるので
設定しておくといいかもね(※ただし必須ではない)というくらいのノリっぽいです。

以下を参考に設定しました。
DNS逆引きレコードを変更する|さくらのVPS|さくらインターネット公式サポートサイト

設定すると nslookup IP.ADD.RE.SS で設定したドメイン名が取得できます。

参考

さくらのVPSに独自ドメインを設定してみた - VPS比較 - Webkaru
2014/10/29

[SublimeText] Sublime Text 3 の検索で日本語をエンターすると文字が消えるのを防ぐ

Sublime Text で地味に困るのが検索窓に日本語を入力した時。
エンターを押した瞬間、入力した文字が消えるので全然検索ができない。
ずっと困ったまま放置していたんだけど
たまたま Twitter で解決方法が流れてきたのでメモ。

参考

Sublime Textの検索窓に日本語を入力する方法
Sublime Text 3 で日本語を検索したとき文字が消える不具合を直す - MEMOGRAPHIX

キーマップを変更してエンターで動作しないようにする

参考元ではコメントアウトされていましたが使えないのは面倒なので
ctrl + enter に全て置き換えました。
// Find panel key bindings
{ "keys": ["ctrl+enter"], "command": "find_next", "context":
    [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
     "context": [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},

// Replace panel key bindings
{ "keys": ["ctrl+enter"], "command": "find_next", "context":
    [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
    "context": [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["ctrl+alt+enter"], "command": "replace_all", "args": {"close_panel": true},
     "context": [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},

// Incremental find panel key bindings
{ "keys": ["ctrl+enter"], "command": "hide_panel", "context":
    [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
    "context": [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
2014/10/24

AndroidStudio で APK を特定のフォルダにコピーする Gradle の設定

AndroidStudio で APK のファイル名を変更する Gradle の設定 | DevAchieveで
APK をリネームしましたが、
出力されるフォルダが build/outputs/apk/ なので
変更したいことがあるかと思います。
Gradle の Task を定義してあげれば
任意のフォルダにコピーする処理を実行することができます。

Gradle で Signed APK とProGuard 関連ファイルをコピーするタスクを設定する


Add move apk task and move proguard task · c7cdd90 · wada811/Android-Material-Design-Colors

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"

                def publish = project.tasks.create("publishAll")

                // Move and Rename APK
                def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
                task.from(output.outputFile)
                task.rename(output.outputFile.name, newName)
                task.into(file("${variant.name}/apk").getAbsolutePath())

                task.dependsOn variant.assemble
                publish.dependsOn task

                // Move ProGuard
                if (variant.buildType.runProguard) {
                    def copyTask = project.tasks.create("copy${variant.name.capitalize()}MappingText", Copy)
                    def buildTypeName = variant.buildType.name
                    copyTask.from(file("build/outputs/proguard/${buildTypeName}").path)
                    copyTask.into(file("${variant.name}/proguard").getAbsolutePath())

                    copyTask.dependsOn variant.assemble
                    task.dependsOn copyTask
                }
            }
        }
    }
}

実行

./gradlew publishAll で
app/release/apk/ に Signed APK が、
app/release/proguard/ に ProGuard 関連ファイルがコピーされます。

参考

AndroidStudioでAPKを作ったあとに特定のディレクトリにAPKをコピーする - Qiita
2014/10/23

AndroidStudio で APK のファイル名を変更する Gradle の設定

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ガイダンスに従えば生成できます。
しかし、GUI からではファイル名は app-release.apk などになるので
変更したい場合などは Gradle の設定が必要になります。

Gradle で Signed APK のファイル名を設定する

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

    applicationVariants.all { variant ->
        if (variant.buildType.name.equals("release")) {
            def file = variant.outputFile
            def applicationId = defaultConfig.applicationId
            def versionCode = defaultConfig.versionCode
            def versionName = defaultConfig.versionName
            def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
            def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
            variant.outputFile = new File(file.parent, newName)
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21+'
    compile project(':library')
}

./gradlew assembleReleaseすると
at.wada811.android.material.design.colors.sample_r2_v1.1.0_20141022_173737.apk
のようなファイル名で app/build/outputs に生成されます。

参考

gradle - AndroidStudioでAPKのファイル名にバージョン番号などを入れる設定 - Qiita

追記: Android Gradle Plugin 0.13, Gradle 2.1 で outputFile が deprecated になっている

WARNING [Project: :sample] variant.getOutputFile() is deprecated. Call it on one of variant.getOutputs() instead. が表示されるので書き直しました。

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            System.println("* output.outputFile.name : ${output.outputFile.name}")
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
                output.outputFile = new File(output.outputFile.parent, newName)
            }
        }
    }
}

outputs というから複数あるのかとおもいきや apk しかありませんでした。
無駄にネストが深くなる…。

参考

android - Gradle warning: variant.getOutputFile() and variant.setOutputFile() are deprecated - Stack Overflow
2014/10/22

AndroidStudio で APK の署名の設定を gradle.properties に記述する

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ウィザードに従えば生成できます。
しかし、CUI から Signed APK を生成したいことがあるかと思います。
また、そのプロジェクトを public リポジトリで管理している場合に
署名に関する設定を公開しないようにするにはどうすればいいのかという問題もあります。

以下に示す方法では、証明に関する設定を gradle.properties に記述し、
.gitignore で gradle.properties を公開しないようにすることで
署名に関する情報を秘密にしておくおことが可能です。

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

...

if (project.hasProperty('storeFile')) {
    android.signingConfigs.release.storeFile = new File(System.getenv('HOME'), storeFile)
}
if (project.hasProperty('storePassword')) {
    android.signingConfigs.release.storePassword = storePassword
}
if (project.hasProperty('keyAlias')) {
    android.signingConfigs.release.keyAlias = keyAlias
}
if (project.hasProperty('keyPassword')) {
    android.signingConfigs.release.keyPassword = keyPassword
}

storeFile=/path/to/your.keystore
storePassword=keystorePass
keyAlias=appAlias
keyPassword=appPass

gradle.properties

new File(System.getenv('HOME'), storeFile) がキモで、
file(storeFile) にするとプロジェクトの相対パスで認識されるから
プロジェクトに keystore を含めるか、無理やり相対パスからたどるかになるんだけど
前者はプロジェクトごとに keystore を入れなければならなくて一元管理できないし、
後者は環境によってパスが変わりうるから微妙になります。
その点、new File(System.getenv('HOME'), storeFile) は $HOME からの絶対パスになるので統一しやすいです。

参考

AndroidStudio - Android Studio(Gradle)でapkファイルを作成する時にstorePassword/keyAlias/keyPasswordの指定方法をいくつか検証してみた。 - Qiita
2014/10/15

[Android]ActionBar のタイトルの文字色を変更する

ActionBar のタイトルの文字色を変えるメソッドはないので
あまり変えてやろうと思うこともないかと思いますが
気まぐれに文字色を変えたくなった時のためにメモしておきます。

Theme でActionBar のタイトルの文字色を変更する

普通の方法ですが、動的に変更することができません。
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.Light.ActionBar">
        <item name="titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

    <style name="AppTheme.ActionBar.TitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:textColor">@color/red</item>
    </style>

</resources>
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.ActionBar">
        <item name="android:titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

</resources>

Programmatically

String title = getResources().getString(R.string.title);
int titleColor = getResources().getColor(R.color.titleColor);
String htmlColor = String.format(Locale.US, "#%06X", (0xFFFFFF & Color.argb(0, Color.red(titleColor), Color.green(titleColor), Color.blue(titleColor))));
String titleHtml = "<font color=\"" + htmlColor +  "\">" + title + "</font>";
getSupportActionBar().setTitle(Html.fromHtml(titleHtml));

Html とかウケる。こんな方法があったとは。

参考
android - ActionBar text color - Stack Overflow
2014/10/05

git diff で JSON を整形して比較する

Git でJSON を整形して比較できるらしいのでメモ。
JSON だけでなく Word や ODT、 Jpeg や PNG なども
実ファイルを比較時に変換させることができるみたいです。

参考
Git - Git Attributes
Git - gitattributes Documentation

設定

*.json diff=json
[diff "json"]
 textconv = "jq ."
2014/09/22

[zsh]peco で git をもっと便利にする

peco を導入しました。すごく便利です。
導入についてはコチラ↓
peco を導入した | DevAchieve

peco で色々フィルタリングして選択できるのは
ファイル、ハッシュ、ブランチを選択する事が多い Git と相性が良さそうです。

peco script を色々作るとエイリアスとか大変っぽいので
zsh のグローバルエイリアスを使ってみました。

編集されたファイルをフィルタリングする

function peco-git-changed-files(){
    git status --short | peco | awk '{print $2}'
}
alias -g F='$(peco-git-changed-files)'

使い方

git add F
Ctrl + Space で複数ファイル選択して add も可能です。

コミットのハッシュをフィルタリングする

function peco-git-hash(){
    # git log --oneline --branches | peco | awk '{print $1}'
    git lg | peco | sed -e "s/^[\*\|][ |\\\/\*]*//g" | awk '{ print $1 }'
}
alias -g F='$(peco-git-hash)'
[alias]
 lg = log --all --graph --decorate --oneline
git lg (log graph) は僕の git log を置き換えるコマンドです。
以下のようにコミットが確認できるので履歴確認、ブランチ確認、マージする際に便利なコマンドです。

peco ではこの色付けがなくなってしまうので残念です。頑張って保持して欲しいところ。

使い方

git diff F とか git cherry-pick F とか。
おおよそコミットのハッシュを指定するところ全てで使えるんじゃないですかね?
git diff で複数選択とか便利です。

ブランチをフィルタリングする

function peco-git-branch(){
    git branch | peco | sed -e "s/^\*[ ]*//g"
}
alias -g B='$(peco-git-branch)'

使い方

git checkout B とか
git merge B とか
git diff B など。

参考

pecoでgit向けの汎用的なフィルターを2つ作成してみた - Qiita
私のpeco活用事例 - k0kubun's blog

[zsh]cdr: 開いたディレクトリの履歴からディレクトリを開く

peco を導入した | DevAchieve で実は cdr を導入していました。
zsh に組み込まれているコマンドに cdr という
開いたディレクトリの履歴からディレクトリを開くコマンドがあります。
~/.zshrc で cdr を有効にしてあげれば使えるようになります。

$HOME/.cache/shell/ は用意しないといけないので
mkdir -p $HOME/.cache/shell/ しておいて下さい。
# cdr, add-zsh-hook を有効にする
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs

# cdr の設定
zstyle ':completion:*' recent-dirs-insert both
zstyle ':chpwd:*' recent-dirs-max 500
zstyle ':chpwd:*' recent-dirs-default true
zstyle ':chpwd:*' recent-dirs-file "$HOME/.cache/shell/chpwd-recent-dirs"
zstyle ':chpwd:*' recent-dirs-pushd true
設定については以下を参考にして下さい。
zsh: 26. User Contributions
zshでcdの履歴管理に標準添付のcdrを使う - @znz blog

使い方

確認
cdr -l
1 ~/.zsh
2 ~/.zsh/peco-scripts
3 ~/Documents/workspace
4 ~/dotfiles

開く
cdr 3

こんな感じですね。
これだけじゃイチイチ開く前に確認しないといけないので peco で選択したいですね。

function peco-cdr() {
    local selected_dir=$(cdr -l | awk '{ print $2 }' | peco)
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N peco-cdr
2行目で何をしているか。
cdr -l
1 ~/.zsh
2 ~/.zsh/peco-scripts
3 ~/Documents/workspace
4 ~/dotfiles
↑を awk でディレクトリ名だけ抜き出して
cdr -l | awk '{ print $2 }'
~/.zsh
~/.zsh/peco-scripts
~/Documents/workspace
~/dotfiles
peco で選択して cd する。
シンプルなコマンドの組み合わせで凄く便利に!
これなら自分でも何か作れそうでワクワクします。

peco を導入した

コマンドラインの行選択ツール peco を導入しました。
peco - Simplistic Interactive Filtering Tool
どんなものかわからないと思うので上のリンクから Demo の gif 画像を見て下さい。
なんとなく便利そうな雰囲気がしますよね?

インストール

それではインストールしてみましょう。Mac では Homebrew で以下の通り。
brew tap peco/peco
brew install peco
それ以外の場合は上のリンクからインストール方法を確認して下さい。
Go で作られているのでインストールが簡単らしいです。

peco のカスタマイズ

peco はパイプでコマンドの出力を行選択して、パイプで次のコマンドに渡すコマンドラインツールなわけだけど
ある程度頻繁に使いそうなものについてはスクリプトファイルを書いておいたほうが便利ですね。
こういうものはどんどん拡張したくなるのでスクリプトファイルの保存場所を決めておきます。
~/.zshrc
~/.zsh/peco-scripts/ の読み込みやキーバインドの設定
~/.zsh/peco-scripts/
peco のスクリプトファイルの保存場所

# load peco scripts (require cdr)
for f (~/.zsh/peco-scripts/*) source "${f}"
% tree peco-scripts
peco-scripts
├── peco-cdr.zsh
└── peco-select-history.zsh

peco-cdr.zsh: 開いたディレクトリの履歴からディレクトリを選択する

[zsh]cdr: 開いたディレクトリの履歴からディレクトリを開く | DevAchieve
function peco-cdr() {
    local selected_dir=$(cdr -l | awk '{ print $2 }' | peco)
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N peco-cdr
# 履歴から cd
bindkey '^@' peco-cdr

peco-select-history.zsh: コマンドの履歴からコマンドを選択する

function peco-select-history() {
    local tac
    if which tac > /dev/null; then
        tac="tac"
    else
        tac="tail -r"
    fi
    BUFFER=$(history -n 1 | eval $tac | peco --query "$LBUFFER")
    CURSOR=$#BUFFER
    # zle clear-screen
}
zle -N peco-select-history
# 履歴からコマンド選択
bindkey '^r' peco-select-history

追記: 2014/12/10
コマンド履歴の重複を削除して peco で選択できるようにしました。
[peco]peco-select-history.zsh で表示されるコマンド履歴の重複を削除する - Qiita

まとめ

使ってみるとわかりますが、すごく便利です!

参考: pecoを使い始めた - $shibayu36->blog;

タグ(RSS)