カテゴリー: 技術メモ

  • Google Documents List API コレクション編

    Googleドキュメントの任意のコレクション配下にファイルをアップロードすべく,少々苦闘してしまいました。

    まず,コレクションやサブコレクションの作成は,Google Documents List API のサンプル通りでうまくいきました。

    次に,任意のコレクションへのファイルのアップロードなのですが,なかなかうまくいきませんでした。

    ResumableUploader の完了イベントに渡ってくる引数の AsyncOperationCompletedEventArgs e には,下記のようにメンバーとして DocumentEntry があります。

        static void OnDone(object sender, AsyncOperationCompletedEventArgs e) {
            DocumentEntry entry = e.Entry as DocumentEntry;
        }

    Adding a resource to a collection に従って,アップロード完了したファイルの  entry->Id を使用して,DocumentService->Insert でコレクションに移動しようとしましたが,全然うまくいきません。

    「デリゲートのコンテキストではAPI呼べないのかな?」とか,「DocumentsRequest->Insertで出来ないかな?」とか,「そもそも ResumableUploader でアップロード先コレクションを指定できないのかな?」とあれこれ試しても,うまくいきません。

    そのうち,デバッガで entry の中身を見てみると,ほぼ空っぽなことに気づきました。ごく一部のメンバーしか値が入っていないようなのです。なぜ?

    そこで,一覧を取得しなおして,アップロードしたファイル名と比較して,一致した場合はコレクションに移動する,という処理を作成しました。

    一応動作していますが,美しくないです。

    4/22 追記

    ファイル名で比較では,同一ファイル名が存在しうるので,その既存のファイルもコレクションに移動してしまうことに気がつきました。全然だめです。

    よくよく検索したところ, Googleグループに回答そのものがありました。

    手元のコードでは,以下のようにUploadUrlを指定することで,任意のコレクション配下にファイルをアップロードできるようになりました。

                String^ s = gcnew String("https://docs.google.com/feeds/upload/create-session/default/private/full/" + parent->DocumentEntry->ResourceId + "/contents?conver=false");

     

  • ListView_EditLabelでキーが効かない

    Win32API ListView_EditLabelマクロでリストビューのアイテムの編集を始められるのですが,ENTERキーやBSキーが効きません。ENTERキーが効かないので,マウスで他のエリアをクリックして編集を確定させなければなりませんでした。これでは使いづらいので,原因を調べました。

    ダイアログの上にリストビューコントロールを作成していると,キーがダイアログの方で処理される,という現象があり,サブクラス化で回避できる,という情報がありました。しかし,Lhazではダイアログアプリではないので,これには該当しません。

    引き続き調べていくと,TranslateAccelerator() がアクセラレータに登録されているキーをWM_COMMANDに変換している,ということを思い出しました。確かに,ENTERキーは個別ファイルを開きますし,BSキーは上位フォルダに移動します。

    どうも簡単に回避する仕組みはないようなので,フラグ変数を設けて,ListView_EditLabel~LVN_ENDLABELEDITの間は,TranslateAccelerator()を呼び出さない,というダサイ解決法に落ち着きました。

  • OpenGrok index が cron で動かない

    OpenGrokのindex更新を午前2時に行うよう,crontabに設定にしたのですが,全く動かないので悩んでいました。数分後に実行されるよう変更して, /var/log/syslog を見ると, grandchild #6356 failed with exit status 2 とエラーが出ていました。エラー出力をファイルに出させると, “Unable to determine Exuberant CTags command name for…” とのことで, ctags が見つからないようです。ここまで来て, PATH の問題だと分かりました。 crontab -e で PATH を通して,無事に自動更新されるようになりました。

     

  • Google Documents List Data API 共有編

    Googleドキュメント上のファイルの共有は,以下のように行います。

    本当は「リンクを知っている全員」への共有を行いたかったのですが,あれこれ設定を変えて試しても,「ウェブ上で一般公開」の共有にしかできませんでした。

        GDataCredentials^ c = gcnew GDataCredentials(email, password);
        RequestSettings^ r = gcnew RequestSettings(appname, c);
        DocumentsRequest^ dr = gcnew DocumentsRequest(r);
        Document^ doc = SortTable[sel]->GetDoc();
        DocumentEntry^ entry = doc->DocumentEntry;
        AclQuery^ query = gcnew AclQuery(entry->AccessControlList);
        AclFeed^ aclFeed = dr->Service->Query(query);
        AclEntry^ aclentry = gcnew AclEntry();
        aclentry->Role = gcnew AclRole("reader");
        AclScope^ scope = gcnew AclScope();
        scope->Type = AclScope::SCOPE_DEFAULT;
        scope->Value = "";
        aclentry->Scope = scope;
        aclFeed->Insert(aclentry);
        dr->Service->Query(query);
  • Gerritを約1年運用してみて

    Gerritを利用して拙作のLhazをオープンソース化してから,1年が過ぎようとしています。ここで,Gerritの良い点,悪い点をまとめます。GitHubやSourceForgeを使用した経験はないので,他の開発プラットフォームと比較した結果ではありません。運用中のレビューサイトはこちらになります。

    良い点

    gitが身についた

    Gerritはgitベースですので,gitの使い方が身につきました。add, commitなどの基本的なコマンドはgitのみの運用でも身につくかと思いますが,Gerritではpushやコンフリクトの解決にrebaseが必要だったりして,やや進んだ使い方が身につきます。また,Lhazの開発では,zlib等の外部ライブラリを使用しており,mergeも使ったりしました。

    ブランチがGerrit上で可視化されるのも良いと思います。具体的には,zlib等をmasterブランチ,lhazブランチと分けて,開発元の更新はmaster,自分で手を加えた部分はlhaz,でトラッキングしていますが,このような感じで流れが分かります。

    コラボレーション

    Gerritでは複数人で開発を進めていきますが,コードレビューしたりされたりで,人の書いたコードをじっくり見ることができ,たいへん参考になります。2ペインで左に更新前,右に更新後,と差分が見やすいのも気に入っています。自分のレビュースタイルは,動いてるならいいんじゃない?,といったあっさりしたものですが,1ライン単位でレビューコメントを記載できるので,ネチネチ派?の方も使い出があるのではと思います。

    Lhaz開発メンバーから文字列クラスのリノベーションをご提案頂き,1プロジェクトとしてインテグレートさせて頂いたのですが,自分には馴染みの少ないテンプレートが駆使されているC++らしいコードで,とても勉強になりました。

    仕事(Androidスマートフォン開発)でもGerritを使用していますが,コミットのアップロードやマージがあった際にメールで通知を受けることができるので,相当広い範囲に目を配ることができます。

    モチベーション

    Gerritにpushして,レビューして,+1 Verified,+2 Looks good to me, approved,のスコアがついた後,「Submit Patch Set」というボタンを押して,変更がブランチにマージされます。この瞬間,小さな達成感があり,気持ちよいです。早く次の変更を入れたくなるので,開発が継続していきます。

    そして,過去のコミットを振り返ると,開発の歴史そのものですので,「よくがんばったな」「続けていこう」という気になれます。この辺りは特にGerrit固有ということはないですが。

    悪い点

    とっつきにくい?

    こちらのWikiにてLhazの開発参加方法を説明していますが,自分で見ても始めるのが面倒くさそうです(実際はそうでもないと思いますが…)。Lhazでこれまでコミット頂けたのもまだ4名の方で,あまり気軽にトライできないのかな,と思われます。

    また,GitHubのように自分で試せる公開Gerritサーバといったものはないので,自分でサーバを用意してGerritをインストールしていくのも,ひと手間いります。 review.chitora.com でホスティングすることも可能ですので,使ってみたい方はご連絡ください。

     コードレビュー専用

    Gerritは純粋なコードレビューツールですので,設計に使用することは難しいです(全てテキストファイルで設計すれば可能かもしれませんが)。また,タスク管理やプロジェクト管理,バグ管理もできません。この辺りは,SourceForge等の方が強いと思います。Lhaz開発では,別途Redmineを使用して,それらの管理を行っています。逆に,コードレビュー専用だけに,セットアップさえ済めば,運用上はボタンを押すだけのシンプルさ,とも言えるかもしれません。

  • 開発をつぶやく

    Gerritのmergeフックを利用して,ソースの変化をTwitterにつぶやく方法です。

    Gerritにソースをpushして,レビューアにapproveされて,Submitボタンを押して変更がマージされると,下記スクリプトが実行されます(「review_site」はGerritのインストール先です)。

    review_site/hooks/change-merged

    実行される際のパラメータは,以下のようになります。

    change-merged --change <change id> --change-url <change url> --project <project name> --branch <branch> --submitter <submitter> --commit <sha1>
    • change id: Gerrit上のURLの連番
    • change url: GerritのURL
    • project name: gitのリポジトリ名
    • branch: マージされたリモートブランチ名
    • submitter: 変更をpushした人
    • sha1: コミットのhash

    ツイート内容として,

    [プロジェクト] コミットコメントのサブジェクト GerritレビューURL
    
    例) [lhaz] Clean up of sort kind/reverse http://review.chitora.com/#change,497

    とつぶやきたい場合,change-mergedスクリプトの内容を以下のようにします。

    #!/bin/sh
    URL=$4
    PROJECT=$6
    SHA1=$12
    cd ../git/$6.git
    TITLE=`git show -s --format="%s" $SHA1`
    twt "[$PROJECT] $TITLE $URL"

    それぞれ引数を変数に引き取り,

    cd ../git/$6.git

    にてgitリポジトリのディレクトリに移動します。hooksから見た相対位置になります。

    TITLE=`git show -s --format="%s" $SHA1`

    はコミットのサブジェクトを取り出すgitコマンドになります。
    twtは自作のコマンドラインでツイートするツールですが,類似のものが使用できると思います。

  • Windows Update で無線LANのドライバが消える

    HP Pavilion Desktop PC s5750jp CTにオプションの 802.11b/g/n 対応ワイヤレスMini card LAN を装着していますが,最近の Windows Update でデバイスドライバが消える,という現象が起きました。OSは,Windows7 64bitを使用しています。

    Ralinkのドライバを更新 (3.1.9.0→3.2.1.0)して一時は解決できたかに見えたのですが,日を開けてPCを起動すると,問題が再発していました。

    このため,現時点では「システムの復元」でPCの状態を少し前に戻して無線LANで接続しています。

    解決しました。ワイアレスマウスの電池が少なくなっていたのが真の原因でした。有線マウスに換えたところ,Windows Update最新の状態で問題なく無線LANが使えるようになりました。お騒がせしました。

    有線マウスでも再現してしまいました。無線LANのHW故障が疑われます。面倒なので外付けのメディアコンバーターを購入します。

  • Google Documents List Data API アップロード編

    無変換で Google Document にファイルをアップロードするためには, ResumableUploader を使います。 file はファイルへのフルパスになります。

    ClientLoginAuthenticator^ cla = gcnew ClientLoginAuthenticator(
        "App",
        ServiceNames::Documents,
        email,
        password
    );
    ResumableUploader^ ru = gcnew ResumableUploader();
    Document^ entry = gcnew Document();
    entry->Title = "タイトル";
    entry->MediaSource = gcnew MediaFileSource(file, "application/octet-stream");
    Uri^ createUploadUrl = gcnew Uri("https://docs.google.com/feeds/upload/create-session/default/private/full/?convert=false");
    AtomLink^ link = gcnew AtomLink(createUploadUrl->AbsoluteUri);
    link->Rel = ResumableUploader::CreateMediaRelation;
    entry->DocumentEntry->Links->Add(link);
    ru->InsertAsync(cla, entry->DocumentEntry, gcnew Object);
  • Google Documents List Data API ダウンロード編

    ファイルのダウンロードには, DocumentsRequest.Download を使用します。以下のサンプルでは,再度リストを取得しています。これは, Document^ の寿命が分かっていないためです。初めのリスト取得で doc->Id が保存してあり, UI でダウンロード対象が m_id として指定されます。今回のリスト取得で比較を行い,一致した場合はダウンロードします。

    また,ネイティブクラスのメンバにマネージドクラスへのポインタ(この場合, Document^ )を持たせる方法もよく分かっていないため,上記方法をとっています。

    Stream からバッファに一気に読み込む処理がありそうなのですが, .NET がよく分かっていないので1KBずつ読み出しています。

    GDataCredentials^ c = gcnew GDataCredentials(email, pass);
    RequestSettings^ r = gcnew RequestSettings("App", c);
    DocumentsRequest^ dr = gcnew DocumentsRequest(r);
    Feed^ feed = dr->GetEverything();
    for each (Document^ doc in feed->Entries) {
        marshal_context^ context = gcnew marshal_context();
        MYSTR id = context->marshal_as(doc->Id);
        delete context;
        if (id == m_id) {
            Stream^ s = dr->Download(doc, "");
            MYBUF buf;
            array^ bytes = gcnew array(1024);
            pin_ptr p = &bytes[0];
            for (;;) {
                int n = s->Read(bytes, 0, 1024);
                if (n == 0)
                    break;
                buf.Add(n, p);
            }
  • Google Documents List Data API リスト編

    Retrieving a list of documents では, DocumentsService を使用してファイル一覧を取得していますが,後でファイルをダウンロードするときに使う DocumentsRequest.Download に Document を渡す必要があるため, DocumentsRequest を使用してフィードを取得します。

    サンプルソースは以下のようになります。

    GDataCredentials^ c = gcnew GDataCredentials(email, password);
    RequestSettings^ r = gcnew RequestSettings("App", c);
    DocumentsRequest^ dr = gcnew DocumentsRequest(r);
    Feed^ feed = dr->GetEverything();
    for each (Document^ doc in feed->Entries) {
        DocumentEntry^ entry = doc->DocumentEntry;
        entry->Title->Text; // タイトル
        entry->Updated;     // 更新時刻
    }