JavaScriptでメソッドチェーンを作る

はじめに

jQueryメソッドチェーンはとても便利で、自分もよく使っていますが「作り方」が よく分かっていなかったので実際に簡単なサンプルを作ってみました。 知っている人にとっては簡単だと思いますが、自分のように知らない人に少しでも お役に立てれば幸いです(`・ω・´)ゞ

そもそもメソッドチェーンとは

メソッドチェーンとは、メソッドをつなげて処理することをいいます。 例えばjQueryだと、

jQuery( 'div' ).width( 100 ).height( 100 ).css( {
   backgroundColor: '#000'
} );

といったようにwidth, height, cssメソッドをつなげて書くことができます。 もし、jQueryメソッドチェーンに対応していないと以下の様な形で書かないといけません。

var div = new jQuery( 'div' ); // 内部的にnewしてるのでつけるかどうか微妙だけど。
div.width( 100 );
div.height( 100 );
div.css( {
    backgroundColor: '#000'
} );

一度変数に入れることが必須となるので少しめんどくさいですね。

メソッドチェーンの基本的な作り方

作り方の前にまずは、文字列をappendメソッドで連結し、getメソッドで連結した文字列を取得する メソッドチェーンに対応していないクラスを作ってみます。

var builder = function( str ) {
    
    if ( typeof str === 'undefined' ) {
    
        this._str = '';

    } else {

        this._str = '' + str; // 型変換
    }
};

builder.prototype = {

    // 文字連結
    append: function( str ) {
        
        this._str += '' + str;   
    }, 

    // 連結した文字列を返す
    get: function() {
        
        return this._str;
    }
}

上記のクラスを使うと

var b = new builder();
b.append( 'hoge' );
b.append( 'fuga' );
b.append( 'moge' );
var result = b.get();
console.log( result );
// > hogefugamoge

このような形で書くことになります。 さて、このbuilderクラスをメソッドチェーンできるようにしてみましょう。

var builder = function( str ) {
    
    if ( typeof str === 'undefined' ) {
    
        this._str = '';

    } else {

        this._str = '' + str; // 型変換
    }
};

builder.prototype = {

    // 文字連結
    append: function( str ) {
        
        this._str += '' + str;
        // 自分自身を返す
        return this; 
    }, 

    // 連結した文字列を返す
    get: function() {
        
        return this._str;
    }
}

このクラスは以下のように利用することができます。

var result = new builder().append( 'hoge' ).append( 'fuga' ).append( 'moge' ).get();
console.log( result );
// > hogefugamoge

メソッドをつなげて書くことが出来ました! どこが変わったかというと、appendメソッドで「return this;」として自分自身を返してあげています。 これだけでメソッドチェーンに対応できるんですね! ただし、これではbuilderの前にnewがついてしまっています。newを取る方法はいろいろあるような 気がしますが、ここではjQueryのやり方にならって書いてみます。

「new」を取る

では、実際にnewをつけない書き方で書いてみましょう。

var builder = function( str ) {
    
    // インスタンスをbuilderの中で生成
    return new builder.prototype.init( str );
};

builder.prototype = {

    // 初期化メソッド( initという名前じゃなくてもいいです )
    init: function( str ) {

        // コンストラクタ的なのをbuilder.prototype.initの中に書く
        if ( typeof str === 'undefined' ) {
    
            this._str = '';

        } else {

            this._str = '' + str; // 型変換
        }
    }, 

    // 文字連結
    append: function( str ) {
        
        this._str += '' + str;
        // 自分自身を返す
        return this; 
    }, 

    // 連結した文字列を返す
    get: function() {
        
        return this._str;
    }
}

// builder.initのprototypeにbuilderのprototypeを持たせる
builder.init.prototype = builder.prototype;

これでnewをつけずに書くことができます!

var result = builder().append( 'hoge' ).append( 'fuga' ).append( 'moge' ).get();
console.log( result );
// > hogefugamoge

ポイントとしては、builderの中で書いていたコンストラクタ的な処理を自身のprototypeの中に 作った関数( ここではinit )にやらせてしまい、その関数のprototypeにbuilderのprototypeを 持たせ、builderの中でインスタンスを生成することでnewを省略することができます。 さて、ここまで分かったところで簡単な電卓クラスを作ってみます。

簡易電卓クラス

たす、ひく、かける、わるのできる簡単な電卓クラスを作ります。 ただし、カッコのついた計算もできるように少し工夫します。

!function( window ) {
// 簡易電卓クラス
// 3 * ( 1 + 3 ) / 6
// みたいな式を
// calc( 3 ).multiply( calc( 1 ).plus( 3 ) ).divide( 6 ).get();
// こんな感じで使えるように
var calc = function( num ) {

    // calcクラスであればそのまま返す
    if ( num instanceof calc ) {

        return num;
    }

    // 数字以外は0にする
    if ( typeof num !== 'number' ) {

        num = 0;
    }
    // インスタンス生成
    return new calc.prototype.init( num );
}

calc.prototype = {

    // コンストラクタ
    init: function( num ) {

        this._num = num;
    }, 

    // たす
    plus: function( num ) {

        this._num += calc( num ).get();
        return this;
    }, 

    // ひく
    minus: function( num ) {

        this._num -= calc( num ).get();
        return this;
    }, 

    // かける
    multiply: function( num ) {

        this._num *= calc( num ).get();
        return this;
    }, 

    // わる( 0でわることを考慮してない )
    divide: function( num ) {

        this._num /= calc( num ).get();
        return this;
    }, 

    // 取得
    get: function() {

        return this._num;
    }
}

// initのprototypeにcalcのprototypeをつっこむ
calc.prototype.init.prototype  = calc.prototype;

window.calc = calc;

}( window );

さて、このクラスを使って簡単な計算をしてみましょう。

// 10 + 20
calc( 10 ).plus( 20 ).get();
// > 30

// ( 9 - 7 ) * 5
calc( 9 ).minus( 7 ).multiply( 5 ).get();
// > 10

// 3 * ( 1 + 3 ) / 6
// 括弧内の計算はcalcでおこないそのオブジェクト自体を引数に指定する
calc( 3 ).multiply( calc( 1 ).plus( 3 ) ).divide( 6 ).get(); 
// > 2

ちょっと工夫すると括弧付きの計算もできるようになりました!

まとめ

メソッドチェーンするには、メソッドの最後に「return this;」とつけて自分自身を返すことで 簡単に作ることができました。使いどころはライブラリとか作らない限りあまりないかもしれませんが 覚えておいて損はなさそうです! calc.jsをgistにあげました。

https://gist.github.com/sawapi/40445b4a5613ac613aa4

HTMLで字詰め(カーニング)できるJS書いてみた

いろいろあってHTMLで字詰めできるライブラリが欲しくなったので
自分で書いてみました。探してみるといろいろライブラリあるみたいですが><
あ、IEでは確認してません。動かなかったらすみませんm(__)m

使い方

使い方は簡単です。
以下のように記述することで、字詰めされた状態のHTMLを取得できます。

var text = "「字詰め」のテストです。"
text = text.kerning(); // 字詰めされたHTMLを取得する
console.log( text );

結果は以下の通りです。

<span style="display:inline-block;margin-left:-0.5em;">「</span>字詰め<span style="display:inline-block;margin-right:-0.5em;">」</span>のテストです<span style="display:inline-block;margin-right:-0.3em;">。</span>

サンプル

JavaScriptの説明はWikipediaから引用しましたm(__)m
http://ja.wikipedia.org/wiki/JavaScript

<!doctype html>
<html>
<head>
<style>
h2 { margin: 0px; padding: 0px; color: #222222; font-size: 18px; }
div { font-size: 14px; color: #444444; border: 1px solid #a0a0a0; padding: 5px; }
</style>
</head>
<body>

<h2>字詰めなし</h2>
<div>
JavaScript(ジャヴァスクリプト)とは、プログラミング言語のひとつである。
Javaと名前が似ているが、異なるプログラミング言語である(後述の#歴史を参照)。
オブジェクト指向のスクリプト言語であることを特徴とする。
主にウェブブラウザに実装され、動的なウェブサイト構築や、
リッチインターネットアプリケーションなど高度なユーザインタフェースの開発に用いられる。
</div>

<h2>字詰めあり</h2>
<div id="kerning">
JavaScript(ジャヴァスクリプト)とは、プログラミング言語のひとつである。
Javaと名前が似ているが、異なるプログラミング言語である(後述の#歴史を参照)。
オブジェクト指向のスクリプト言語であることを特徴とする。
主にウェブブラウザに実装され、動的なウェブサイト構築や、
リッチインターネットアプリケーションなど高度なユーザインタフェースの開発に用いられる。
</div>

</body>
<script src="./kerning.js"></script>
<script>
var element = document.getElementById( 'kerning' );
element.innerHTML = element.innerHTML.kerning();
</script>
</html>

字詰めした結果は以下の通りです。
f:id:sawapi:20140612192124p:plain

まとめ

対応している文字が20種類くらいと少ないので、まだまだ見た目はあれなんですが
少しはましになったかと思います。
ライセンスとかはないのでこんなのでよければ自由に(商用でも)使ってくださいm(__)m

SwiftでPush通知

SwiftでPush通知を実装してみました。
証明書の取得等はこちらを参考にしています。

iOSでプッシュ通知を実装する方法の超詳細まとめ(前編)】
http://www.lancork.net/2013/08/how-to-ios-push-first/

iOSでプッシュ通知を実装する方法の超詳細まとめ(後編)】
http://www.lancork.net/2013/08/how-to-ios-push-second/

コードの解説

Push通知を有効にする

iOS SDK8.0ではregisterForRemoteNotificationTypesメソッドがDeprecatedになっているため
registerForRemoteNotificationsメソッドを使います。

func application( application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary? ) -> Bool {
        
    var types: UIUserNotificationType = UIUserNotificationType.Badge |
        UIUserNotificationType.Alert |
        UIUserNotificationType.Sound
    
    var settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil )
    
    application.registerUserNotificationSettings( settings )
    application.registerForRemoteNotifications()
        
    return true
}

iOS7以下では、Push通知を有効にするタイミングで有効にするオプションを
設定していましたがiOS8以上では別で設定します。

  • UIUserNotificationType.Badge・・・アイコンバッジ
  • UIUserNotificationType.Sound・・・Push通知時になる音
  • UIUserNotificationType.Alert・・・Push通知時に表示される文字列

UIUserNotificationSettingsのcategoriesはドキュメントが見当たらなかったのでよくわかっていません。。
上記の処理が成功するとapplication didRegisterForRemoteNotificationsWithDeviceTokenが呼ばれるのでその中でデバイストークンを取得します。

ちなみにiOS7以下では以下のように記述します。

func application( application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary? ) -> Bool {
        
    application.registerForRemoteNotificationTypes(
        UIRemoteNotificationType.Badge | 
        UIRemoteNotificationType.Sound |
        UIRemoteNotificationType.Alert )
        
    return true
}
デバイストークンの取得
func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) {
    
    // <>と" "(空白)を取る
    var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
    
    var deviceTokenString: String = ( deviceToken.description as NSString )
                                        .stringByTrimmingCharactersInSet( characterSet )
                                        .stringByReplacingOccurrencesOfString( " ", withString: "" ) as String
    
    println( deviceTokenString )
    
}

devideToken.descriptionには、

<00000000 11111111 22222222 33333333 aaaaaaaa bbbbbbbb cccccccc dddddddd>

というような文字列で入っているため、"<"と">"と" (スペース)"を取る必要があります。
Swiftの文字の整形の仕方が分からず、、一旦NSStringに変換し、余分な文字を取り除きました。
( いい方法がありましたら教えて下さい。。。)

上記までを実行するとデバイストークンを取得できるのでメモしておきます。

Push通知が届くかテスト

Push通知がちゃんと届くかテストするために以下のスクリプトでテストしてみました。

<?php
$deviceToken = '************';
 
// 送信する文字列
$alert = 'Push test.';
 
// バッジ
$badge = 1;
 
$body = array();
$body['aps'] = array( 'alert' => $alert );
$body['aps']['badge'] = $badge;
 
// SSL証明書
$cert = '********.pem';
 
$url = 'ssl://gateway.sandbox.push.apple.com:2195'; // 開発用
//$url = 'ssl://gateway.push.apple.com:2195'; // 本番用
 
$context = stream_context_create();
stream_context_set_option( $context, 'ssl', 'local_cert', $cert );
$fp = stream_socket_client( $url, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $context );
 
if( !$fp ) {
 
    echo 'Failed to connect.' . PHP_EOL;
    exit( 1 );
 
}
 
$payload = json_encode( $body );
$message = chr( 0 ) . pack( 'n', 32 ) . pack( 'H*', $deviceToken ) . pack( 'n', strlen($payload ) ) . $payload;
 
print 'send message:' . $payload . PHP_EOL;
 
fwrite( $fp, $message );
fclose( $fp );

以下のように通知がきたら成功です。
https://gist.githubusercontent.com/sawapi/a7cee65e4ad95578044d/raw/c456ba27a7926244375e8b496c9c3c6787e911ce/screenshot_push.png

Swiftで音楽プレイヤー作ってみた

みんなSwiftでなにか作ってるので作りたくなって。。(´・ω・`)
Objective-C分からなさすぎてiOSアプリ自体ほとんど作ったことないですが、
Swiftはすんなりできました。(だいぶはまったけど><)

つくったもの

だいぶシンプルな音楽プレイヤー。
https://raw.githubusercontent.com/sawapi/musicPlayer/master/screenshot.png
iPhoneに入ってる曲を取り出してタップすると再生します。

ファイル構成

  • AppDelegate.swift・・・そのまま
  • ViewController.swift・・・Viewの処理
  • SongQuery.swift・・・iPhoneに入っている曲の情報を返す
  • Main.storyboard・・・NavigationControllerとTableViewおいただけ

コードの概要

曲リストの取得
func get() -> AlbumInfo[] {
    
    var albums: AlbumInfo[] = []
    
    // アルバム情報から曲を取り出す
    var albumsQuery: MPMediaQuery = MPMediaQuery.albumsQuery()
    var albumItems: MPMediaItemCollection[] = albumsQuery.collections as MPMediaItemCollection[]
    var album: MPMediaItemCollection
    
    for album in albumItems {
        
        var albumItems: MPMediaItem[] = album.items as MPMediaItem[]
        var song: MPMediaItem
        
        var songs: SongInfo[] = []
        
        var albumTitle: String = ""
        
        // album情報から曲リストを取得する
        for song in albumItems {
            
            albumTitle = song.valueForProperty( MPMediaItemPropertyAlbumTitle ) as String
            
            var songInfo: SongInfo = SongInfo(
                albumTitle: song.valueForProperty( MPMediaItemPropertyAlbumTitle ) as String,
                artistName: song.valueForProperty( MPMediaItemPropertyArtist ) as String,
                songTitle:  song.valueForProperty( MPMediaItemPropertyTitle ) as String,
                // NSNumberじゃないとうまくいかない
                songId:     song.valueForProperty( MPMediaItemPropertyPersistentID ) as NSNumber
            )
            
            songs.append( songInfo )
        }
        
        var albumInfo: AlbumInfo = AlbumInfo(
        
            albumTitle: albumTitle,
            songs: songs
        )
        
        albums.append( albumInfo )
    }
    
    return albums 
}

MPMediaQuery.albumsQuery()でアルバムのリストを取得できます。
アーティストを取得したい場合は、artistsQuery()で取得できます。
songIdは、UInt64で動くと思ったらなぜか動かず。。NSNumberなら動きました。

ナビゲーションにボタン追加
override func viewDidLoad() {
        
    super.viewDidLoad()
        
    let stopButton: UIBarButtonItem = UIBarButtonItem( title: "Stop", style: UIBarButtonItemStyle.Plain, target: self, action: "stop" )
        
    self.navigationItem.rightBarButtonItem = stopButton        
}

func stop() {
    // 処理
}

ドキュメントだとaction: Selectorってなっててなに入れるのかよく分からなかったですが
string型で指定すればいいみたいです。
http://stackoverflow.com/questions/24007650/selector-in-swift
ここ参考にしました。

選択した音楽を再生
func tableView( tableView: UITableView?, didSelectRowAtIndexPath indexPath:NSIndexPath! ) {        
    
    // MPMediaItemPropertyPersistentID
    // 曲を識別するID
    let songId: NSNumber = albums[indexPath.section].songs[indexPath.row].songId
    
    // IDからMPMediaItemオブジェクトを取得
    let item: MPMediaItem = songQuery.getItem( songId )
    
    let url: NSURL = item.valueForProperty( MPMediaItemPropertyAssetURL ) as NSURL
    
    //var error: NSError? = nil
    
    // 再生
    audio = AVAudioPlayer( contentsOfURL: url, error: nil )
    audio.play()
    
    self.title = albums[indexPath.section].songs[indexPath.row].songTitle
}

MPMediaItemのvalueForPropertyメソッドはAnyObjectを返すので型変換してあげます。
AVAudioPlayerのerrorの部分にNSErrorを入れてもうまくいきませんでした。。
やり方が分かったら直します。

Swiftを使ってみて

Objective-CはとっつきにくくてiOSアプリの開発を半分諦めてましたが
Swiftはドキュメントを見ながらさくさく作ることが出来ました。
コードの補完がちゃんとされるようになればかなり楽に開発できると思います。
これまでAndroidアプリしか作ってこなかったですが、これでiOSアプリにも挑戦できそうです。