何度やっても同じ

ただの日記

Mac(Mojave)にjava6と7と8をインストール

お仕事の都合でJava 6と7と8をインストール。

support.apple.com

Java6はこれ、しかたない。7と8はhomebrewで。AdoptOpenJDKでいこうと思ったけれどJava7はなかったので7はZuluのをインストール

$ brew tap AdoptOpenJDK/openjdk
$ brew cask install adoptopenjdk8

$ brew tap homebrew/cask-versions
$ brew cask install zulu7

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (4):
    1.8.0_222, x86_64:  "AdoptOpenJDK 8"    /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
    1.7.0_232-zulu-7.31.0.5, x86_64:    "Zulu 7"    /Library/Java/JavaVirtualMachines/zulu-7.jdk/Contents/Home
    1.6.0_65-b14-468, x86_64:   "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
    1.6.0_65-b14-468, i386: "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home

$ export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
$ java -version
openjdk version "1.7.0_232"
OpenJDK Runtime Environment (Zulu 7.31.0.5-CA-macosx) (build 1.7.0_232-b6)
OpenJDK 64-Bit Server VM (Zulu 7.31.0.5-CA-macosx) (build 24.232-b6, mixed mode)

CSSのfont-familyに長いこと悩んで最近こうなった

ヒラギノ角ゴとメイリオの並び順については、Macメイリオがインストールされている場合よりも、Windowsヒラギノがインストールされている場合のほうが結果が悲惨、ということもあり、メイリオを先にもってくるケースが多いらしい(下記URL参照)。自分も基本的にはこんな感じでフォント指定していた。

http://loconet.web2.jp/blog/archives/2007/02/cssfontfamily.html
http://webbingstudio.com/weblog/think/entry-137.html

ところが、いつだったか、ここはてなブログソースコードを眺めていて、こんな記述に気付いた。

if (~navigator.userAgent.indexOf('Mac OS X')) {
  document.write('<style type="text/css">html, body { font-family: \x27Helvetica\x27, \x27Arial\x27, \x27ヒラギノ角ゴ Pro W3\x27, \x27Hiragino Kaku Gothic Pro\x27, sans-serif; } </style>');
} else {
  document.write('<style type="text/css">html, body { font-family: \x27Helvetica\x27, \x27Arial\x27, \x27メイリオ\x27, \x27Meiryo\x27, \x27MS PGothic\x27, sans-serif; } </style>');
}

JavaScriptでOSを判別してフォントを選択してます。「おー」と思って、その後はこれをパクっていた。

技術的な内容の文章を書く場合は、日本語の文章中に現れる英単語は等幅にしたいので、HelveticaとかArialのところはTwitter Bootstrapのcode要素のスタイルをパクってMonaco, Menlo, Consolas, 'Courier New' と並べることがよくあった。


そして最近、@font-face の local() とかいうのを知って、あーこれ便利だなと。@font-face はWebフォントのために使うものというイメージしかもっていなかったけれど、ローカルにある複数フォントに優先度つけた上でそれに名前をつける、ような使い方ができるとのこと。ユニコードの範囲でフォントを分けることもできるらしい。

@font-face {
  font-family: OreOreFont;
  src:
    local('メイリオ'),
    local('Meiryo'),
    local('ヒラギノ角ゴ Pro W3'),
    local('Hiragino Kaku Gothic Pro'),
    local('MS PGothic'),
    local(sans-serif);
}

@font-face {
  font-family: OreOreFont;
  src:
    local('Monaco'),
    local('Menlo'),
    local('Consolas'),    
    local('Courier New'),
    local('Courier'),
    local(monospace);
  unicode-range: U+0000-007F;
}

html,body {
  font-family: OreOreFont;
}

OSを判別できるわけではないので、ヒラギノメイリオの並び順問題を解決できるわけではないけれど、どこかのサーバに自分専用CSSを置いていくつかフォントを定義しておけばコピペせずに使いまわせるので、便利。並び順はもうどうでもよくなってきた。必要ならこれとJavaScriptによるOS判別を併用することもできるし。メイリオが嫌いなマカーはメイリオいれなきゃいいいいよね。

GAEでTwitterみたいなフォロー関係を実現する - fanout problem

http://dl.google.com/io/2009/pres/W_0415_Building_Scalable_Complex_App_Engines.pdf

古い資料だけど。何か驚くべき手法が書かれているのか?と思って読んだらそういうわけでもなく。大勢のフォロワーに向けてパブリッシングするメッセージエンティティに、フォロワーのキーをリストプロパティでもっておけということだった(下記コードはイメージです)。

public class Message implements Serializable {
  private Key key;
  private Key senderKey;
  private String body;
  private List<Key> followerKeys;
}

あとからフォローしたりアンフォローした場合は、タスクでせっせと過去メッセージのfollowerKeysを書き換えるんでしょう。

エンティティあたりインデックス数の制限により、この設計ではフォロワー数の上限が10,000(インデックス数上限20,000として)になってしまうけれど、どうせそんな規模にはそうそうならないでしょう。そこだけ許容してしまえば、とてもシンプルに作れそう。

もうひとつ問題があって、リストプロパティのデシリアライズコストが馬鹿にならないらしい。この資料が作成された当時はどうにもならなかったようだけど、今のGAEにはProjectionクエリがあるので、followerKeysは取得せず欲しいプロパティだけを取得することができる。これはこれでまた制約があって、たとえばProjectionクエリで取得するプロパティはインデックス作成対象になっていなければならないので、Text型などは使えなかったりするのだけど。

oauth/access_token にアクセストークンを要求した結果と、SignedRequest内に格納されているアクセストークンで、expiresの数値の意味が違うメモ

パラメータとしてcodeを渡してoauth/access_tokenにアクセストークンを要求した場合、expiresの値として返ってくるのは、例えば↓くらいの数値で、これはトークン生成時からの有効期限(単位:秒)を表している。この例の秒数は日にち換算するとちょうど60日くらい(facebookのドキュメントで long-lived user access_token と呼ばれている期限長めのタイプ)。

access_token=xxx&expires=5183998

一方、ページタブ組み込みのアプリで使うSignedRequestではどういう値なのかというと、例えば↓のような具合で、expiresには期限切れ日時自体がセットされている(これは1970年1月1日からの秒数)。

{ expires=1361682000, issued_at=1361675975, oauth_token=xxx, ... }


めんどい。

Java + facebook Graph API メモ

Graph API は restfb

Graph API の呼び出しはrestfb。きれいに設計されていてとても使いやすい。オブジェクトをGETしたいときは、オブジェクトの種類を問わずfetchObjectまたはfetchConnectionを使う。これはすべてのオブジェクトを一意なIDで管理していて型(?)におおらかなfacebook側の設計と親和性が高い。もちろん、生JSONとかMAPが返ってきてもイヤなので、型は引数で指定。

User user = facebook.fetchObject("me", User.class, 
        Parameter.with("fields", "id,name,location"));

使いたいフィールドが、restfbによって提供されているモデルオブジェクトに含まれていなければ、自分で拡張すればよい。たとえば、Userクラスにはpictureフィールドがない(プロフィール画像が取れない!)ので、そんな場合はこうやって…

ExtendedUser user = facebook.fetchObject("me", ExtendedUser.class, 
        Parameter.with("fields", "id,name,location,picture"));

ExtendedUserを自作する。facebookが返すpictureフィールドは値がオブジェクトなので、対応するクラスが必要。返ってくるデータの形式さえつかんでいれば、あとは機械的作業。

public class ExtendedUser extends User {

    private static final long serialVersionUID = 1L;

    @Facebook
    private Picture picture;
  
    public Picture getPicture() {
      return picture;
    }

    public void setPicture(UserPicture picture) {
      this.picture = picture;
    }
}

public static class Picture implements Serializable {

    private static final long serialVersionUID = 1L;

    @Facebook
    private Data data;

    public Data getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = data;
    }

    public static class Data implements Serializable {

        private static final long serialVersionUID = 1L;

        @Facebook("is_silhouette")
        private boolean isSilhouette;

        @Facebook
        private String url;

        public boolean isSilhouette() {
            return isSilhouette;
        }

        public void setSilhouette(boolean isSilhouette) {
            this.isSilhouette = isSilhouette;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }
}

ログイン処理は自分で書いてしまう

restfbはログイン処理してくれないので、ここは自分で書いてしまう。こんな感じで使えるFacebookLoginクラスを作った。あとで公開しとく。かも。

public class IndexController extends BaseController {

    @Override
    protected Navigation run() throws Exception {

        AccessToken accessToken = sessionScope("access_token");
        if (accessToken != null && !accessToken.isExpired()) {
            return forward("index.jsp");
        }

        FacebookLogin login =
            FacebookLogin.Builder
                .with(FacebookService.APP_ID, FacebookService.APP_SECRET)
                .setCallbackUrl("https://apps.facebook.com/xxx/")
                .setPermissions(Permission.values())
                .begin();

        String code = asString("code");
        if (StringUtil.isEmpty(code)) {
            AccessToken token = login.getAccessToken(code);
            sessionScope("access_token", token);
            return forward("index.jsp");
        }

        return html(login.getLoginDialogRedirectScript());
    }
}

それはそうと、写真へのタグ付けでひっかかった

写真へのタグ付けを行って成功すると、facebookから次のようなレスポンスが返ってくる。

true

trueって何だよ…。wktkしてJSONを期待しているrestfbがこれをくらうとパースエラー吐くので、restfbを直さないといけない。GitHubで公開されているプロジェクトは、pom.xmlが少し変(ソースフォルダの設定がないとか)な気がするけど、build.xmlもついてるしビルドはしやすい。

jQueryの謎: new jQuery.fn.init と jQuery.prototype

jQueryのソースを読んでいて、よく理解できない点が2つ。軽く調べて、一応の結論を得たのでメモっておく。

まずは2つの問題をまとめておく。

new jQuery.fn.init 問題

問題の箇所を2.0.0b1から抜粋すると、以下のとおりなのだけど

line:51~

  jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context, rootjQuery );
  },

端的に言えば、ここで new をしないで

jQuery = function( selector, context ) {
  return this.init( selector, context, rootjQuery );
},

こう書いたら何か問題あるのか、という話。こうすれば、jQuery.fn.init などではなく、きれいにjQuery(をコンストラクタとする)オブジェクトが返ってくるのに。

jQuery.prototype 問題

おなじく問題の箇所を2.0.0b1から抜粋。

line:85~

jQuery.fn = jQuery.prototype = {

このように、jQuery.prototype として jQuery.fn が設定されているわけだけど、new jQuery.fn.init 問題のところでみたとおり、関数jQueryは内部で new した別のオブジェクトを返すので、仮にこれをコンストラクタとして new jQuery() みたいなコードを書いても、jQuery.prototype を継承するオブジェクトは作れない。

じゃあ jQuery.prototype を設定する意味なくない?という話。

以下この2つの問題について考えていく。

昔のjQueryオブジェクトはホントにjQueryオブジェクトだった

いわゆるjQueryオブジェクトというのはjQuery.fn.initをコンストラクタとして作られたオブジェクトなわけだけど、実はこれはjQuery1.2.2以降の話で、jQuery1.2.1までのjQueryオブジェクトは、文字通り関数jQueryをコンストラクタとして作られたオブジェクトだった。1.2.1の関数jQueryを見てみると次のとおり。

var jQuery = window.jQuery = function(selector, context) {
  // If the context is a namespace object, return a new object
  return this instanceof jQuery ?
    this.init(selector, context) :
    new jQuery(selector, context);
};

thisがjQueryインスタンスかどうか判定して分岐しているけれど、これは要するに、jQuery("selector") と書いても new jQuery("selector") と書いても同様に、関数jQueryをコンストラクタとするオブジェクトが得られるよ、ということ。new を使わない前者の書き方だと this が window になるので、this instanceof jQuery は false となり、結局 new jQuery 呼び出しになるわけだ。

これが今のようなコードに変わったのが1.2.2です(下記)。jQuery.fn.init じゃなくて jQuery.prototype.init だったりはするけれど、この両者は同じオブジェクトだし、そこは気にしない方向で。今問題にしたいのは、このときから正確な意味でのjQueryオブジェクトが返されなくなったということ。

var jQuery = window.jQuery = function( selector, context ) {
  // The jQuery object is actually just the init constructor 'enhanced'
  return new jQuery.prototype.init( selector, context );
};

もっと昔はinitメソッドなんてなかった

さらに歴史をさかのぼると1.1.2まではjQueryオブジェクト初期化の役割をもつインスタンスメソッドとしてのinitメソッドは存在しなかった。かわりに、関数jQueryに初期化コードがベタっと書かれている。

この部分の設計が大きく変わったのは1.1.3。そこで、関数jQuery自体はシンプルになり、かわりにjQuery.fn(ひいてはjQuery.prototype)に init メソッドが定義された。

var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this || !this.init )
		return new jQuery(a,c);
	
	return this.init(a,c);
};

jQuery.fn = jQuery.prototype = {
	init: function(a,c) { 
                // ... 略

これは単に、JavaScriptでクラス型オブジェクト指向的なコードを書くプログラミングスタイルに移行したということだろうか。インスタンスメソッドと同列に init とか initialize みたいな名前のメソッドを定義して、それをコンストラクタから呼ぶスタイルに変わった。

今問題にしている視点だけから言えば、この時代のコードは直感的でわかりやすい。jQueryオブジェクトは実際に関数jQueryをコンストラクタとして作られたオブジェクトであり、jQuery.prototype にそのメソッドが定義されている。jQuery.prototype には jQuery.fn という別名がつけられており、拡張したいときはjQuery.fnに関数を追加する。という感じです。

jQuery.prototype問題の答え

1.2.1までは関数jQueryがそのままjQueryオブジェクトのコンストラクタだったので、jQuery.prototype を設定することには意味があった。今ではそれが不要だとしても、逆に設定しないことに必要性があるわけでもないし一応残しておくか、という判断なのかな、というのがとりあえずの結論です。いわゆる「歴史的な理由」。

new jQuery.fn.init の理由は高速化・・・らしいけど

new jQuery.fn.init というコードが追加された1.2.2とはどういうリリースだったのか、もうちょっと詳しく見てみよう。

jQueryブログによると、バグフィックスなどのほかに $(DOMElement) 呼び出しの高速化が行われている。これは怪しい。

しかし、1.2.2の init メソッドには

    // Handle $(DOMElement)
    if ( selector.nodeType ) {
      this[0] = selector;
      this.length = 1;
      return this;

というコードが追加されているので、$(DOMElement) の高速化ってのはこのコードのことを指しているのだろう・・・などと思いつつ、しかし new jQuery.fn.init の理由は結局よくわからないので、githubの履歴を漁って、該当のコミットを探してみた。

https://github.com/jquery/jquery/commit/f97f77c034dc62001a687c728bdfdc71a23bf6b8

John Resig自身による修正。理由は、$(DOMElement) の高速化・・・あ、いや、all uses of $(...) と言っているので、DOMElementに限らないのかもしれない。実際この修正で $(DOMElement) の実行速度だけが改善されるというのはよくわからない。$(DOMElement) の改善はやっぱりこっち(下記)がメインだと思うんだよね・・・。

https://github.com/jquery/jquery/commit/1a2fdafd386a8f7be8b633634a684969921f8b8f

$(...) 全般の速度改善だとしても、なんでこれで改善するのかよくわからないが。

ともかく何故速くなるのかはよくわからないけど、理由は高速化だったということで、忙しいのでこのへんでノ