たぷつきません

おなかがでてきた。もうたぷついてるやん。

だいやりーやーめたー

もう日記よりツイッターが多いし、ここほとんどつぶやきが転送されるだけにしか使ってないので辞める。

つぶやき以外で沢山書きたい場合やコードを貼り付けたい時は、シンタックスハイライトよかGistがいいので、はてなブログたぷつきません)に引越します。

こちらのはてなダイヤリーは過去ログ置き場として放置プレイ。

iPhoneをUSBメモリ代わりに使う

 会社と自宅の行き来で、巨大ファイルを持ち運びたくなってググッたら発見。http://www.macroplant.com/iexplorer/
 自宅のネットワークがヘボくて、何度やっても829MBあるiPhoneの5.0.1アップデートがダウンロード途中でコケてた。会社でダウンロードしてiPhone内に今保存できたので、自宅に持ち帰ってからアップデートする予定。ナイスっ!

ブックマークレットがバグる

 ちょっと前のバージョンではブックマークレットのタイトルが空になるという不具合があって、ようやく直ったかと思ったら、今度は、Mac Chromeの最新版(14.0.835.202)では、ブックマークレットのURL内のJSが過度にエスケープされてChrome上で実行できないものになってしまっている。なんだかなー。

追記(11/04)

15.0.874.106 にアップデートされたら治った模様。ヨカッタヨカッタ。

appengineのサーバー間通信で楽したいためのフレームワーク

 時間がないから作れないけど頭の中のイメージを書いておく。
・appengineのアプリと別のappengineのアプリでのRPC。サーバーとサーバーの通信。
・URLFetchの10秒制限から離れて実現する。GWT-RPCのように要求側(あくまでもappengineサーバー)から見てレスポンスはAsyncCallbackする。そんなわけで10分以内であればTaskQueueを、それも超えるような場合はBackendsのTaskQueueを使うようにサーバー側が調整できる。要するにサーバー側からクライアントに返すデータはHTTPのResponseではなくRequestで「後で」返す。
・クライアント(あくまでもappengineサーバーの)側は、CPUを占有したくない。Backendsに常駐しちゃってもいいじゃん的なことではなく。単発的に起きる事象にも対応したいため。なので応答をcronでポーリングなんて無駄なのでしない。
 で、これを実現するために、要求者には自身をDatastoreにシリアライズした上で「死んでもらう」。


...なんかごちゃごちゃするので、取説から書いてみよう。このヘンテコRPCのハローワールドを。名前考えてないからとりあえずHenteko。とりあえず利用者の手数が少なくなるアノテーション前提で書いてみる。ただアノテーションプロセッサ、自分で書く気が起きないけど。オーバーヘッドが少なければリフレクションでもいいかな。必要に応じてなにかをextendsしたり、その辺りはまあどうとでも。

1.まずweb.xmlにHentekoを登録(フィルタなりサーブレットなりコントローラを登録することになるんだろうけど割愛)。
2.とりあえずGWT-RPCみたいにインターフェースとAsyncインターフェースを用意する感じで。

@HentekoService
public interface GreetingService {

    String getMessage(String myName);

}
public class GreetingServiceImpl implements GreetingService {

    @Queue
    public String getMessage(String myName) {
        // すっげー時間が掛かってなんとか処理が終わったりすることもあれば、
        // 一瞬で終わることもあってもOKな感じで。
        return "Hello " + myName;
    }

}

 @Queueアノテーションでそのメソッドをどのキューで実行するかを決める。@Queue("キューの名前")のように。これによりBackendsのキューも使える(Target指定の定義をしたキューにより)。引数なしはデフォルトキュー。Queueアノテーションを書かない場合は、そもそもTaskQueueにしない?(要検討。面倒ならデフォルトキュー)。

3.クライアント側の作成。GWT-RPCのようにペアとなるAsyncインターフェースとしておきましょうか。

public interface GreetingServiceAsync {

    @Server("サーバーのID")
    void getMessage(String myName, @Queue AsyncCallback<String> callback);

}

 メソッドごとにサーバーを指定することも。どっかにConfigがあって識別子に対応する サーバーを指定できる。
 server1=https://xxxx.appspot.com のように。設定はxmlでもJsonでもどーでも。あ、違う。運用時にサーバーを差し替えとかできるようにするために、Datastoreとハイブリッドが良い。
 で、クライアントの制御クラスを作成。かならず Serializableとすること。
 それと callbackにも@Queueアノテーションを付けられる。付けなければ通常リクエストだが、付けた場合は
 コールバックメソッドの実行はTaskQueue内で実行となる。もちろんこちらも引数でキュー名を指定できるようにし、
 長時間の処理であればBackendsも利用可能。
 @Serverに複数のサーバーIDを渡してレスポンスをリストで連結して戻してもらいたいという要求もあったりするけど、
 とりあえずそれはいいや。突き詰めるとHadoopになりそうだし目的が違う感じ。

@HentekoClient
public class Greeting implements Serializable {

    private transient GreetingServiceAsync service = Henteko.get(this, GreetingServiceAsync.class);

    // シリアライズしたフィールドでステートを持てる

    public void helloWorld() {
  
        // この辺りで final なローカル変数とか使われてもコールバックに渡らないからダメ。


        :
        serivce.getMessage("Japan!!", new AsyncCallback<String>() {
            
           public void onSuccess(String result) {
               // finalなローカル変数は使えないけど、Greetingクラスのフィールド(デシリアライズできたもの)は扱える。
               
               // ここのコードでは、helloWorldを呼び出されたときのHttp requestはアテにならない(最初のリクエスト時の情報を一書に保存してエミュレートしても良いけど...)。
           }
            
        }); 

    }

}

 
 と、まあ、使い方はGWT-RPCっぽい。Henteko.get() で、要求者を把握。サービスメソッドの実装内でシリアライズ

通信

通信内容の仕様もXmlだろうとJsonだろうと復元できればどうとでも。
1.要求者→サーバー。
 メソッド指定とパラメータをpostして依頼。
 応答は、ユニークな受け付けキー。要求者はそれとともにデータストアに要求クラスのインスタンスシリアライズ

2.サーバー→要求者
 受け付けキーとメソッドの戻り値をpostして応答。
 要求側は受け付けキーを元にデータストアからクラスインスタンスを復元し、コールバックメソッドを呼び出す。

iTunesがiPhoneの古い内容を表示しちゃう

 iMaciTunesに同期したくないiPhoneをつなげたら、iPhone内の音楽が聴けるしプレイリストも出てきて再生ができて重宝していたんだけど、これって初回に出た内容から一切更新されない。iPhone内のミュージック、ムービー、プレイリストは変更されているのに、その情報でリフレッシュしてくれていない感じ。ミュージックだけリフレッシュしているような気がする。
 個人のマシンでだけ完全な同期をしたいのでリードオンリーなまま使用したい。あまり変な操作して中身ふっ飛ばしたくない。どこにキャッシュしちゃってるんだろう。そのキャッシュを削除するにはどうすれば良いんだろう。

追記

 AppCleanerでiTunesを完全アンインストールして、iTunesを入れなおしてもやっぱりダメ。Appleに報告仕様にもインシデント取ろうとしてくるし。iMac内のどこかに残っているということだと思うけど、デバイスのプロファイル情報とかなんだろうそういうのはどこにあるんだよ〜〜!!

Apps + Document List APIで、ドメイン間でドキュメントを配布するには

 Doc List APIでは、コピー元ドキュメントのキーを指定して、コピーを作成できる。APIはこちら
 しかし実はいきなり直接コピーができない。過去に相手ユーザーのドキュメントを参照した経験があるユーザーであればコピーが成功し、初めて関係を持とうとしたユーザーではUnknown errorが発生した*1
 これを解決するには配布先メンバーの閲覧権限をドキュメントのACL追加すれば良い。私の場合は両ドメイン共にアプリ(Marketplaceアプリ)の登録者であることが前提だったので、2LOでそれぞれのドメイン用のDocsServiceのインスタンスに順に操作(一方に権限設定後もう一方にコピー実行)すれば良かった。その後にACLから配布先メンバーの権限を削除しても、そのドキュメントに「リンクを知っているユーザーなら誰でもリードオンリーで開ける」権限を設定しているため、2回目以降はACL操作は行わずにコピーAPIを動作させられるようになった。
 これを行えるようにするには、Appsのドキュメント設定が以下を満たしていなければならない。
1.配布先は「共有オプション」が「共有できます」にチェックがあるか、「共有できません」でも「組織の外部からドキュメントを受け取ることを許可する」にチェックが入っていること。そうでなければ最初のACL追加APIは下記の非常に分かりやすい例外を吐いて失敗する。Unknown errorじゃないので助かる。

com.google.gdata.util.InvalidEntryException: The administrator for {target domain} has disabled the ability to receive docs from outside their domain. If those you're attempting to share with would like more information, they should contact their domain administrator directly.

2.配布元は「共有できます」にチェックがある上に「ユーザーがドキュメントをウェブに公開したり、一般公開や限定公開のドキュメントとして一般に公開したりできるようにします。」もチェックが必要。

 ちなみにコピー作成時間はドキュメントのサイズに比例するので、GAEでこれをやるのであればURLフェッチの10秒タイムアウトなんてすぐに到達する。タスクキューとメール通知で回避するのがヨサゲ。

*1:ひょっとしたら他に理由があるのかもしれない。ここで記述するのはあくまで少ないサンプルでの経験則。

Apps Scriptのプロパティの依存関係

 ユーザープロパティは、全プロジェクトに属さないアカウントに紐付くプロパティで、横断的に利用出来る。プロジェクトAがセットして、プロジェクトBが参照するということもできる。
 プロジェクト(スクリプト)プロパティは、もちろんプロジェクトに属するので、共有者の誰が見ても同じ内容が格納されていることになる。
 スプレッドシートの「コピーを作成」すると、どちらのプロパティも複製されないので、ユーザープロパティもプロジェクトプロパティも空っぽになる。

Apps Scriptのトリガの依存関係

 トリガの依存関係というかプロジェクト(スクリプト*1の依存関係が分かりにくすぎるので整理する。
 まずはTriggersメニューから。
「現在プロジェクトのトリガー」と「全てのトリガー」があるが、後者の「全てのトリガー」とは、開いているスプレッドシートに紐付いている全てのプロジェクトを指している。そのスプレッドシートにしかないものもあれば、ギャラリーから取得できる複製もある。
 スプレッドシートの「コピーを作成」すると、全てのプロジェクトもコピーが作成される。なので、複数スプレッドシートから1つのプロジェクトを参照することはできない*2
 次に、トリガー設定画面上に表示される、各トリガの話。
 まずはシンプルイベント(onOpen,onEdit,onInstall)から。スプレッドシートから起動時に実行されるトリガーとしていて依存関係としてはこのトリガの所有者はプロジェクト自身になる。onOpenで言えば、「onOpenというメソッド名がある」ことが条件で自動的に追加される。現在プロジェクトのトリガだろうと、全てのプロジェクトのトリガだろうと関係なく、いくつものプロジェクトにonOpenがあっても1行だけ操作できない状態で表示される。onOpenが定義されていないプロジェクトからは「現在のプロジェクトのトリガー」でも「全てのトリガー」でも何も表示されない。実行コンテキスト上の権限は実際に開いたり編集したりインストールしたユーザーのアクセス者権限で行われる。実行者権限はスクリプトを書いたオーナーになる。しかしOAuthの承認などアクセス権をもらいたいコードの場合、スクリプトマネージャからの直接実行しないと「承認が必要です。」と言われるだけでケアしてくれない*3
 時刻主導型トリガは、ユーザーに紐付けられる。エラー時の通知先もアクセス中のユーザー宛になる。このためシンプルイベントのようにスクリプトが独立して動作できる定周期のトリガが作れない。
 これもスプレッドシートの「コピーの作成」で失われる。シンプルイベント以外の手動で追加したトリガーはどれもコピーされない。残念。

*1:Google自身文言がスクリプトと言ったりプロジェクトと言ったり一定じゃない。例えば現在のスクリプトのトリガーをクリックすると現在のプロジェクトのトリガーというタイトルの画面が出てくる

*2:というわけで、バグがあってスクリプトに変更を加えたら以前複製したスプレッドシート内の同じ名前のスクリプトにも同じ変更を加えないといけない。コード(プロジェクト)とデータ(スプレッドシート)は分離できずセットになる

*3:こちらに遷移してくれないという意味です。

なんか文字化けると思ったら。

 よく以下のような感じになる。他のブラウザだと表示されるのに。

表示する優先言語がサーバーに渡ってないんじゃないかな?...とぐぐったらMacSafariこういうことだった
 これで...治るかな...

スプレッドシートで本当に隠しておきたいもの付きで配布

 スプレッドシートに共同編集者を設定はしたいがオーナー以外には隠しておきたい情報がある場合、スクリプトプロジェクトの共有を「ドキュメントの共同編集者にプロジェクトの編集を許可する」のチェックは付けないようにしておく。
 こうするとオーナー以外はスクリプトエディタを開いて[ファイル]-[プロパティ]を開いても、「プロジェクトのプロパティ」を見ることができない。「ユーザープロパティ」しか見えない。この情報はコード内から、ScriptPropertiesサービスで参照できるのでスクリプトコードにハードコードしないようにすれば良さげ。例えば、なかじまんさんがApps Scriptで2Legged OAuthを実現させていたので、consumerKeyとconsumerSecretをScriptPropertiesに保存しておくのが良い使い方かも。
 …と思ってたんだけど、複製したらプロパティの内容は消失することが判明。残念。
 コピーを作成で複製すると、プロジェクトも一緒に複製されるので、リードオンリーだった共同編集者も複製したものであれば編集できるようになる*1
 サービス公開を有効にして外からプロパテイを流しこむという手もあるんだけど、「サービス公開を有効に」するAPIが無さそう。やりたいことはDocument List APIで複製したスプレッドシートに対してプログラムから操作したいので...
 ちなみにサービスとして公開というのは、doGet(e) をメソッドして定義しておけば呼び出してくれるというもの。こちらが参考になった。本家はこちら
 しかし実行してみたら...オワタ

Google ドキュメントでサーバー エラーが発生しました。ページをリロードしても解決しない場合は、お問い合わせください。

この問題やその他の問題についてディスカッションするには、Google ドキュメント ヘルプフォーラムにアクセスしてください。報告されている問題のリストを確認するには、Google ドキュメントの報告されている問題ページをご覧ください。


ご不便をおかけして申し訳ありません。

追記(9/8)

…って、違う違う。終わってない。ScriptPropertiesサービスを触るコードを書いたら上記のエラー画面になっただけでした。そういう意味では、サーブレットとして実行する場合に利用出来るサービスが何なのかが分からないと辛いですねー。

*1:タイムラグがあるようで、複製後いきなりスクリプトコードエディタを開くと空っぽになっていることが多い。時間が経つと複製されたプロジェクトが見える

IE9が酷い

 new Button();

だけで「(null): DOM Exception: INVALID_CHARACTER_ERR(5)」という例外が発生する。

とりあえず例のごとくmetaタグでIE8をemulateすれば回避できたが、なんなんだろう。

追記

って違ーう!違わないけど、違ーう!GWT2.3.0であれば問題ないことがわかった。ちゃんと対応している。
GWT2.1.1なんて古いのを使っているのが問題。でもgwt-gadgetがバージョン追随してくれないんだよー。頼むよGoogle

トリコロール

うっかり網棚八兵衛な僕は、ふだん手ぶら出勤なのだけど、もろもろやらずに寝ちゃったので、MBAと延長保障のをバッグに詰めて家を出た。

最近ゲットしたバッグは初披露でちょっとアガる。