Underscore.jsおさらい1(概要)

Underscoreのドキュメントを見直していたら、「これ使えてたわ」ということがあったので、機能一覧ぐらいは頭に叩き込むためにおさらいする。

参考
Underscore.js
遅すぎたUnderscore.js入門 - 全体像 - Qiita

全体概要

Underscoreはいわゆる便利系関数のJavaScriptライブラリ。
jQueryは不要で単体で使えるのが手軽。Backbone使うなら必須。

機能概要

  • Collections
    • forEachやソートといった、配列などの集合データの操作系
  • Arrays
    • 配列操作系、Collectionsは『配列+ハッシュ配列(オブジェクト)』が対象なのに対して、Arraysは純粋な配列のみ
  • Functions
    • JavaScriptではよくある関数を引数に取る関数群
  • Objects
    • keyやvalueの一覧取得、clone、extendといったオブジェクト操作系
  • Utility
    • ユーティリティ機能
  • Chaining
    • 関数の戻り値を別の関数の引数に・・・とすると階層が深くなって困るので、メソッドチェーンで書けるようにできる
    • Javaな人はJava8のコレクションでstream APIを想像すれば分かりやすいかも

context引数

引数に[context]がある場合は、contextとして渡したオブジェクトが関数内でthisとして扱える。
ざっくりループ系の機能についている感じ。

var obj = {a: 0};
_.each([1, 2, 3], function(element){this.a += element }, obj);
=> obj.a = 6

Backbone.Validationが空文字を許してくれない

Backboneでvalidateを簡単にしてくれるプラグインBackbone.Validation。
GitHub - thedersen/backbone.validation: A validation plugin for Backbone.js that validates both your model as well as form input

正規表現で入力値をチェックできるpatternを使ったら、空文字もエラー扱いになって困った。
ドキュメントのAdding custom validatorsを参考に、独自patternを作って対処。

_.extend(Backbone.Validation.validators, {
  customPattern: function(value, attr, customValue, model) {
    if(value !== ''){
      return Backbone.Validation.validators.pattern(value, attr, customValue, model);
    }
  }
}

空文字でなければ元からあるpatternバリデーション(Backbone.Validation.validators.pattern)を呼び出すだけ。
これでビルトインのnumberやemailパターンも使えるし、独自正規表現パターンも使える。
拡張方法が明記されているのはありがたい。

まーしゃる

先のエントリーにあるメソッドmarshalの意味を調べる。

marshal:(名詞) 元帥

なんだそりゃ、と思ったが動詞だと「(人・軍隊を)整列させる、整頓する」という意味らしい。
だから元帥なのね。
戦いの意味合いを持っているなら、マーシャルアーツのマーシャルはこの単語?

違った・・・martial arts
martial:(形容詞) 勇ましい、好戦的な

ただ発音は同じらしい。

それにしてもなんでarts(芸術)なんだろう、と思ったらWikipediaに書いてあった。
「武芸」が英語になったからか。
マーシャルアーツ - Wikipedia

マーシャルアーツと言えば「テリー・ボガード」が連想される古い人間のワタシ。

Jersey(MOXy)でJSONバインドをカスタマイズする

JSONをリクエストで投げてJersey(MOXy)でPOJOとして受ける際に、Integerのフィールドに空文字が来ると「0」に変換される。
0じゃなくてnullで受けたい、というように変換をカスタマイズした場合はXmlAdapterを継承したadapterを作る。

参考:JAXBのXmlAdapterを自作する - Programming Studio

JerseyというかJAXBの仕様らしい。なのでアノテーションやクラスがXml・・・となってJSONで使うには違和感があるがちゃんと動作する。

  • marshal():POJO => JSON(XML)の変換処理
  • unmarshal():JSON(XML) => POJOの変換処理 をそれぞれ実装する。

で、@XmlJavaTypeAdapterでadapterクラスを指定する。
@XmlJavaTypeAdapterは@Target(value={PACKAGE,FIELD,METHOD,TYPE,PARAMETER})となっているので、指定する箇所で有効範囲が変わる。

いちおう試して動いたが、結局Backbone.stickitのonSetで空文字 => null変換して済ませちゃったのは内緒の話。

BeanManagerを取得する on Weld, Tomcat

Weld 2.3.1 + Jersey2.22.1
Tomcat8

発端はEntityに登録者IDや更新者IDを埋め込みたかったが、Entityで@SessionScopedなBeanを@Injectできなかったから。
EntityListener使ってみたり紆余曲折あったが、時間が無いので経過は省くとして結局BeanManagerを取得して直接Beanを取ってくることに。

参考その1:CDIのBeanManagerを使う - CLOVER

InitialContext ic = new InitialContext();
BeanManager bm = (BeanManager) ic.lookup("java:comp/BeanManager");
Set<Bean<?>> beans = bm.getBeans(SessionDto.class);
Bean<?> bean = bm.resolve(beans);
SessionDto sessionDto = (SessionDto) bm.getReference(bean, SessionDto.class, bm.createCreationalContext(bean));
// SessionDtoが@SessionScopedなクラス

これではlookup()するところでNamingExceptionになってできず。
おそらくTomcatで動かしているからだと思われる。

参考その2:jsf - BeanManager on Apache Tomcat 7.0.47 cannot create resource instance - Stack Overflow
Tomcatの場合、"java:comp/BeanManager"ではなく"java:comp/env/BeanManager"でlookupするとある。
確かにTomcatでDataSourceをlookupする時は"java:comp/env/・・・"で見に行く。
あとcontext.xmlにもResourceを追加する。

 <Resource name="BeanManager"
        auth="Container"
        type="javax.enterprise.inject.spi.BeanManager"
        factory="org.jboss.weld.resources.ManagerObjectFactory" />

で、どうにかSessionDtoが取れた。
紆余曲折時間:約2時間

backbone.validationメモ

参考:thedersen/backbone.validation · GitHub
backbone 1.2.0
backbone.validation 0.11.5

backbone自体にvalidation機能はあるが、処理をいちいち書かないといけないし複雑化すると後から読むのも大変。
なのでbackbone.validationプラグインを使ってスッキリさせる。

Model

バリデーターはModelに「validation」プロパティで指定する。
key=Modelのkey、 value=バリデーター。
処理を記述する場合はfunctionを指定。戻り値にエラーメッセージを返す。

var model = Backbone.Model.extend({
  validation: {
    name: function(value, attr, computedState) {
      if (value == '名無しの権兵衛') return '名前不正';
    }
  }
});

もしくは別でfunctionを作っておいてfunction名を指定。

  validation: {
    name: 'validateName'
  },
  validateName: function() {(略)}

よくあるバリデーターが用意されている。

  validation: {
    name: { required: true }, // 必須
    agreement: { acceptance: true }, // 許諾(true or 'true'のみ可。「同意しますか?」チェックボックスなんかで)
    age: { min: 1 }, // 最小(以上)
    age2: { max: 100 }, // 最大(以下)
    zipcode: { length: 7 }, // 桁数
    password: { minLength: 8 }, // 最小桁数
    password2: { maxLength: 256 }, // 最大桁数
    password3: { rangeLength: [8, 256] }, // 桁数範囲
    language: { oneOf: ['jp', 'en', 'others'] }, // どれかに該当
    passwordRe: { equalTo: 'password' }, // 他のattributeと同値(passwordRe == 'password'ではなく、passwordRe == password)
    mail: { pattern: 'email' }, // 正規表現パターン(この場合はプラグイン定義済みのe-mailパターン)
    weight: { pattern: 'number' }, // 数値(小数も可)
    code: { pattern: 'digits' }, // 数字のみ(≠整数) /^\d+$/
    url: { pattern: 'url' }, // url
    word: { pattern: /^[A-Z]/ } // 独自正規表現
  }

エラー時のメッセージは用意されているが、独自メッセージを出したい場合はmsgを指定。
というかデフォルトメッセージは英語なので全部指定することになる。

    name: {
      required: true,
      msg: '名前は必須です' // functionで文字列return、でも可
    }

複数バリデーター指定もできる。

    name: {
      required: true,
      minLength: 2,
      msg: '名前は必須で2文字以上'
    }

バリデーター毎にメッセージを変えたい場合は配列指定。

    name: [{
      required: true,
      msg: '名前は必須'
    },{
      minLength: 2,
      msg: '名前は2文字以上'
    }]

bind

Viewにバインドする。
バインドするタイミングは、model or collectionがinitializeされた後ならいつでもいい。
Marionette + stickitを使っていて、onShow()でstickitのbindをしてあるので、bind並びでvalidationもそこでbindすることにする。

onShow: function() {
  this.stickit(); // stickitのbind
  Backbone.Validation.bind(this); // validationのbind
}

Backbone.Validation.unbind(view)でunbindされる。

Modelのoverride

bindするとmodelのvalidate()とisValid()がオーバーライドされるので、自分で実装する必要がない。処理を書く手間が省けるのはやはり有難い。

あとpreValidate()が実装され、modelにsetする前に事前検証ができるようになる。
と言ってもstickit使っているとpreValidate()の使いドコロが無い。
とりあえずsave前にvalidate()を呼んでエラー表示することにする。

ポート番号8888

「昨日アップされたソース取り込んで動かしたら変なサイトに飛ぶんだけど」

いやそんなの仕込んだ覚えないし。でも本当に「free-merchants.com」というサイトに飛ばされている。それに自分のローカル環境では起きない。

chromeプラグインが悪さしてんのかなー」と思いプラグインを外してもらうと正常に。
どうやらこれが該当する様子。
New Tab Page redirects localhost:8888 to spam website
日付は2日前、バージョンアップでプラグイン周りがおかしくなったのかも。

確かに開発のローカル環境でwebサーバーを8888ポートであげていた。
プラグイン外して現象は起きなくなったが、念のためポートを変えることにする。
wikiに49152–65535がプライベート ポート番号と書いてある。
TCPやUDPにおけるポート番号の一覧 - Wikipedia

「へー、知らなかったなー」(吉田羊)
「あれ?知らなかったんですか?」(田中みな実)

吉田羊ほんとよく見るなー。ポカリのCMわりと好き。

・・・とりあえずプライベート番号から選んでおく。

結局chromeの現象ってプラグインが悪いのか本体が悪いのかどっちなんだ?
あんまり話題になってないてことはプラグイン側か?
プラグイン便利は便利だけど、やっぱり程々にしないと危険。