Typeを作ってみた。
前に構想した「Type」機能をちょっと実装してみました。Typeの生成とチェックAPIのみ。コンテナにはまだ組み込んでいません。あとテストも途中までしかできてないです。
こんな感じで使います。
// テスト用モデル var TestObject = function(){} TestObject.prototype = { A: function(){}, B: function(){}, C: function(){} }; var TestObject2 = function(){} TestObject2.prototype = { B: function(){}, C: function(){} }; var obj = new TestObject(); var obj2 = new TestObject2(); var assert = YAHOO.util.Assert; // ユーテイリティへの参照 var t = container.types; // メソッドA,Bを持つType var type = t.has( "C", "A" ); // isImplementor() でオブジェクトが指定されたメソッドを持つか評価する。 assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); // メソッドA,またはCを持つType type = t.hasAny( "C", /A/ ); // 正規表現も指定できる。また、引数の数に制限はない。 assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); // Typeのネストも可能 type = t.has( "C", t.hasAny( "A", /X/) ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( "C", t.has( "A", /X/) ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) );
- container.typesで定義されたユーティリティAPIを利用して作成する。
- has()で「指定された引数の条件のメソッドをすべて持つオブジェクトにマッチするType」を生成。
- hasAny()で「指定された引数の条件のメソッドのいずれかを持つオブジェクトにマッチするType」を生成する。
- has(),hasAny()の引数では「文字列」「正規表現」「Type」のいずれかを指定できる。
- 引数の数に制限はない。
実装
container.jsより抜粋。クラス構成は検索条件とかでよく見るヤツです。
/** * タイプ */ container.Type = function() {} container.Type.prototype = { /** * 同じタイプであるか評価します。 * @param {container.Type} that タイプ * @return 同じであればtrue */ equals: function( that ) {}, /** * オブジェクトがTypeの条件を満たすか評価します。 * @param {Object} obj オブジェクト * @return 条件を満たす場合true */ isImplementor: function( obj ) {} } /** * ユーテイリティ */ container.types = { /** * 指定されたメソッドをすべて実装することを示すTypeを生成する。 * @param {String or Regexp or container.Type} methods メソッド名、正規表現またはcontainer.Type * @return 指定されたメソッドをすべて実装することを示すcontainer.Type */ has: function() { return new container.inner.types.And( container.types._createTypes( arguments )); }, /** * 指定されたメソッドのいずれかを実装することを示すTypeを生成する。 * @param {String or Regexp or container.Type} methods メソッド名、正規表現またはcontainer.Type * @return 指定されたメソッドのいずれかを実装することを示すcontainer.Type */ hasAny: function() { return new container.inner.types.Or( container.types._createTypes( arguments )); }, /** * 指定された型の条件を満たさないことを示すTypeを生成する。 * @param {Type} type Type * @return 指定された型の条件を満たさないことを示すType */ not: function( type ) { return new container.inner.types.Not( container.types._createType(type) ); }, _createTypes: function ( list ) { var types = []; for ( var i=0; i < list.length; i++ ) { types.push( container.types._createType( list[i] )); } return types; }, _createType: function ( item ) { if ( item instanceof RegExp ) { return new container.inner.types.RegexpMethod(item); } else if ( item instanceof container.Type ) { return item ; } else if ( typeof item == "string" ) { return new container.inner.types.Method(item); } else { throw "illegal argument."; } } } container.inner.types = {} /** * 完全一致 */ container.inner.types.Method = function( name ) { this.name = name; } container.inner.types.Method.prototype = new container.Type(); container.inner.types.Method.prototype.equals = function( that ) { if ( !that || !( that instanceof container.inner.types.Method ) ){ return false; } return this.name == that.name; } container.inner.types.Method.prototype.isImplementor = function( obj ) { return typeof obj[this.name] == "function"; } /** * 正規表現一致 */ container.inner.types.RegexpMethod = function( exp ) { this.exp = exp; } container.inner.types.RegexpMethod.prototype = new container.Type(); container.inner.types.RegexpMethod.prototype.equals = function( that ) { if ( !that || !( that instanceof container.inner.types.RegexpMethod ) ){ return false; } return this.exp.ignoreCase == that.exp.ignoreCase && this.exp.global == that.exp.global && this.exp.source == that.exp.source; } container.inner.types.RegexpMethod.prototype.isImplementor = function( obj ) { for ( var key in obj ) { if ( typeof obj[key] == "function" && this.exp.test( key ) ) { return true; } } return false; } /** * And */ container.inner.types.And = function(types) { this.types = types; } container.inner.types.And.prototype = new container.Type(); container.inner.types.And.prototype.equals = function( that ) { if ( !that || !( that instanceof container.inner.types.And ) ){ return false; } if ( this.types.length != that.types.length ){ return false; } for ( var i=0; i < this.types.length; i++ ) { var a = this.types[i]; var b = that.types[i]; if ( !a.equals(b) ) { return false; } } return true; } container.inner.types.And.prototype.isImplementor = function( obj ) { for ( var i=0; i < this.types.length; i++ ) { if ( !this.types[i].isImplementor(obj)) { return false; } } return true; } /** * Or */ container.inner.types.Or = function(types) { this.types = types; } container.inner.types.Or.prototype = new container.Type(); container.inner.types.Or.prototype.equals = function( that ) { if ( !that || !( that instanceof container.inner.types.Or ) ){ return false; } if ( this.types.length != that.types.length ){ return false; } for ( var i=0; i < this.types.length; i++ ) { var a = this.types[i]; var b = that.types[i]; if ( !a.equals(b) ) { return false; } } return true; } container.inner.types.Or.prototype.isImplementor = function( obj ) { for ( var i=0; i < this.types.length; i++ ) { if ( this.types[i].isImplementor(obj)) { return true; } } return false; } /** * Not */ container.inner.types.Not = function(type) { this.type = type; } container.inner.types.Not.prototype = new container.Type(); container.inner.types.Not.prototype.equals = function( that ) { if ( !that || !( that instanceof container.inner.types.Not ) ){ return false; } return this.type.equals( that.type ); } container.inner.types.Not.prototype.isImplementor = function( obj ) { return !this.type.isImplementor(obj); }
テストケース
テストケースも一応。NotとTypeのネストのテストが未。
var TypeTest = new YAHOO.tool.TestCase({ name: "TypeTest", setUp: function () {}, tearDown: function () {}, /** * container.types.has()のテスト。 */ testHas: function () { var assert = YAHOO.util.Assert; var t = container.types; var TestObject = function(){} TestObject.prototype = { A: function(){}, B: function(){}, C: function(){}, hoo: function(){}, X: "", Y: 1 }; var TestObject2 = function(){} TestObject2.prototype = { B: function(){}, C: function(){}, foo: function(){}, X: "", Y: 1 }; var obj = new TestObject(); var obj2 = new TestObject2(); var type = t.has( "A" ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.has( "C" ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.has( "X" ); assert.isFalse( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.has( /A/ ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.has( /[a-zA-z]oo/ ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.has( /[a-zA-z]ooo/ ); assert.isFalse( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); assert.isTrue( t.has( "A" ).equals(t.has( "A" )) ); assert.isFalse( t.has( "A" ).equals(t.has( "B" )) ); assert.isFalse( t.has( "A" ).equals(t.has( /A/ )) ); assert.isTrue( t.has( /A/ ).equals(t.has( /A/ )) ); assert.isFalse( t.has( /A/ ).equals(t.has( /B/ )) ); assert.isFalse( t.has( /A/ ).equals(t.has( "A" )) ); type = t.has( "C", /B/ ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.has( /[a-zA-z]oo/, "A", "B" ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); assert.isTrue( type.equals(t.has( /[a-zA-z]oo/, "A", "B" )) ); assert.isFalse( type.equals(t.has( /[a-zA-z]oo/, "X", "B" )) ); assert.isFalse( type.equals(t.has( /[a-zA-z]oo/, "B" )) ); assert.isFalse( type.equals(t.has( "B" )) ); assert.isFalse( type.equals(t.has( /A/ )) ); assert.isFalse( type.equals(t.hasAny( /[a-zA-z]oo/, "A", "B" )) ); }, /** * container.types.hasAny()のテスト。 */ testHasAny: function () { var assert = YAHOO.util.Assert; var t = container.types; var TestObject = function(){} TestObject.prototype = { A: function(){}, B: function(){}, C: function(){}, hoo: function(){}, X: "", Y: 1 }; var TestObject2 = function(){} TestObject2.prototype = { B: function(){}, C: function(){}, foo: function(){}, X: "", Y: 1 }; var obj = new TestObject(); var obj2 = new TestObject2(); var type = t.hasAny( "A" ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( "C" ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.hasAny( "X" ); assert.isFalse( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( /A/ ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( /[a-zA-z]oo/ ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.hasAny( /[a-zA-z]ooo/ ); assert.isFalse( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); assert.isTrue( t.hasAny( "A" ).equals(t.hasAny( "A" )) ); assert.isFalse( t.hasAny( "A" ).equals(t.hasAny( "B" )) ); assert.isFalse( t.hasAny( "A" ).equals(t.hasAny( /A/ )) ); assert.isTrue( t.hasAny( /A/ ).equals(t.hasAny( /A/ )) ); assert.isFalse( t.hasAny( /A/ ).equals(t.hasAny( /B/ )) ); assert.isFalse( t.hasAny( /A/ ).equals(t.hasAny( "A" )) ); type = t.hasAny( "C", /B/ ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); type = t.hasAny( /X/, "A", "Y" ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( /[a-zA-z]oo/, "A", "B" ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); assert.isTrue( type.equals(t.hasAny( /[a-zA-z]oo/, "A", "B" )) ); assert.isFalse( type.equals(t.hasAny( /[a-zA-z]oo/, "X", "B" )) ); assert.isFalse( type.equals(t.hasAny( /[a-zA-z]oo/, "B" )) ); assert.isFalse( type.equals(t.hasAny( "B" )) ); assert.isFalse( type.equals(t.hasAny( /A/ )) ); assert.isFalse( type.equals(t.has( /[a-zA-z]oo/, "A", "B" )) ); }, testBasic : function() { var TestObject = function(){} TestObject.prototype = { A: function(){}, B: function(){}, C: function(){} }; var TestObject2 = function(){} TestObject2.prototype = { B: function(){}, C: function(){} }; var obj = new TestObject(); var obj2 = new TestObject2(); var assert = YAHOO.util.Assert; // ユーテイリティへの参照 var t = container.types; // メソッドA,Bを持つType var type = t.has( "C", "A" ); // isImplementor() でオブジェクトが指定されたメソッドを持つか評価する。 assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); // メソッドA,またはCを持つType type = t.hasAny( "C", /A/ ); // 正規表現も指定できる。また、引数の数に制限はない。 assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); // Typeのネストも可能 type = t.has( "C", t.hasAny( "A", /X/) ); assert.isTrue( type.isImplementor(obj) ); assert.isFalse( type.isImplementor(obj2) ); type = t.hasAny( "C", t.has( "A", /X/) ); assert.isTrue( type.isImplementor(obj) ); assert.isTrue( type.isImplementor(obj2) ); } });