2008/09/26


tokuhiromさんやkazeburoさんの言うように私の所もFastが速かった。
perl の memcached libraries の速度検証 - TokuLog 改めB日記

ワタシの環境だと、Cache::Memcached::Fast の方が速いようだ。

個人的には、 mixi のような大きなサービスでの採用実績があり、速度的にも速い C::M::Fast が現時点ではいい選択肢だとおもう。C::M::libmemcached は、まだまだ開発発展途上な感じなので、もうちょい落ち着くまでは仕事では使えない印象だし。

http://d.hatena.ne.jp/tokuhirom/20080926/1222408445

結果は以下の通り。
Module Information:
 + Cache::Memcached => 1.24
 + Cache::Memcached::Fast => 0.12
 + Cache::Memcached::libmemcached => 0.02008
 + Memcached::libmemcached => 0.2101

Library Information:
 + libmemcached => 0.24

Server Information:
 + localhost:11211 => 1.2.1

Options:
 + Memcached server: localhost:11211
 + Include no block mode (where applicable)? :NO

Prepping clients...

==== Benchmark "Simple get() (scalar)" ====
                  Rate perl_memcached   libmemcached memcached_fast
perl_memcached  2319/s             --           -83%           -86%
libmemcached   13387/s           477%             --           -18%
memcached_fast 16244/s           601%            21%             --
==== Benchmark "Simple get_multi() (scalar)" ====
                 Rate perl_memcached   libmemcached memcached_fast
perl_memcached  664/s             --           -81%           -91%
libmemcached   3509/s           428%             --           -52%
memcached_fast 7306/s          1000%           108%             --
==== Benchmark "Serialization with get()" ====
                  Rate perl_memcached   libmemcached memcached_fast
perl_memcached  2104/s             --           -77%           -81%
libmemcached    8964/s           326%             --           -19%
memcached_fast 11114/s           428%            24%             --
==== Benchmark "Simple get() (w/compression)" ====
                 Rate perl_memcached   libmemcached memcached_fast
perl_memcached  972/s             --           -43%           -43%
libmemcached   1706/s            75%             --            -1%
memcached_fast 1720/s            77%             1%             --
==== Benchmark "Simple set() (scalar)" ====
                  Rate perl_memcached   libmemcached memcached_fast
perl_memcached  5316/s             --           -55%           -71%
libmemcached   11851/s           123%             --           -36%
memcached_fast 18389/s           246%            55%             --
==== Benchmark "Simple set() (w/seriale)" ====
                 Rate perl_memcached   libmemcached memcached_fast
perl_memcached 3190/s             --           -49%           -52%
libmemcached   6201/s            94%             --            -7%
memcached_fast 6668/s           109%             8%             --
==== Benchmark "Simple set() (w/compress)" ====
                Rate perl_memcached   libmemcached memcached_fast
perl_memcached 315/s             --            -2%            -8%
libmemcached   322/s             2%             --            -6%
memcached_fast 342/s             8%             6%             --
Cache::Memcached::Fastのwin32版は、まだ汚いポーティングなので、後日codereposにあげる予定。
Posted at by



2008/09/25


オリジナルはperl版でGTKを使ったUIになっています。
memcachedclient-perl
これを、python(pygtk)、ruby(ruby-gnome2)、lua(lua-gtk)、C(GTK)、java(Swing)に移植してみた。
オリジナルから小さいコードなので、簡単なものですが...
興味のある方は、この辺を覗いて下さい。
Posted at by



2008/09/19


手順だけ。
cd C:\temp\
wget http://search.cpan.org/CPAN/authors/id/T/TI/TIMB/Memcached-libmemcached-0.2101.tar.gz
zcat Memcached-libmemcached-0.2101.tar.gz | tar xv
cd Memcached-libmemcached-0.2101
mkdir src_inst\include\libmemcached
mkdir src_inst\lib
svn co http://svn.coderepos.org/share/lang/c/libmemcached-win32/libmemcached-latest
cd libmemcached-latest\libmemcached
mingw32-make -f makefile.w32
cd ..\..
copy libmemcached-latest\libmemcached\*.h src_inst\include\libmemcached\.
copy libmemcached-latest\libmemcached\memcached.a src_inst\lib\.
copy src_inst\include\libmemcached\*.h src\libmemcached\libmemcached\.
perl Makefile.PL
set OPT="INC=-IC:\temp\Memcached-libmemcached-0.2101\src_inst\include" "LMCD_BUILT_LIB=C:\temp\Memcached-libmemcached-0.2101\src_inst\lib\memcached.a" "LDFROM=$(OBJECT) C:\temp\Memcached-libmemcached-0.2101\src_inst\lib\memcached.a"
nmake %OPT%
あとは libmemcached-latest\libmemcached\memcached.dll
をパスの通る場所に置いておけばWindowsで use strict;
use Perl6::Say;
use Memcached::libmemcached qw(
    memcached_create
    memcached_server_add
    memcached_set
    memcached_get
);

my $key = "foo";
my $value = "bar";
my $memc = memcached_create();
memcached_server_add($memc, '127.0.0.1');
memcached_set($memc, $key, $value);
say memcached_get($memc, $key);
こんなソースが通る様になります。Cache::Memcached::libmemcachedも動きます。
さらに言うなら、rubyのmemcachedも動きました。 require 'memcached'
$cache = Memcached.new('127.0.0.1:11211')
$cache.set('foo', 'bar')
print $cache.get('foo')
こちらはCOMPATIBILITYファイルを弄ったり、最新バージョンで無くなったWHEELな処理をカットしないといけませんが...
さらにさらにpython-libmemcachedも動きます。
import cmemcached

c = cmemcached.Client(['127.0.0.1:11211'])
c.set("foo", "bar")
print c.get("hoge")
こちらもcmemcached.pyxでWHEELな処理をカットしないといけませんが...
とりあえず、動くって事です。
ちなみにオフィシャルの反応はまだなし...
Posted at by



2008/09/18


色々やってたら、出来た。これで が動く様になるのではないか!
地味な作業だったけど、以外と効果は大きいかも。今日は時間無いので明日以降にソース綺麗にしてオフィシャルにパッチを送ってみようかと思ってます。
ちなみにUNIXドメインソケットなmemcachedは動きません。(win32ですからね...)

mingw32でコンパイルすると、こんなソースがコンパイル実行出来ました。
#include <winsock2.h>
#include <memcached.h>
#include <stdio.h>

int main(void) {
    memcached_return rc;
    memcached_st *memc;
    char* value;
    int value_length = 0;
    int flags = 0;

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
        return -1;
    }
    memc = memcached_create(NULL);;
    rc = memcached_server_add(memc, "127.0.0.1", 11211);
    printf("server add: %s\n", memcached_strerror(memc, rc));

    rc = memcached_set(memc, "test", 4, "example", 8, 0, 0);
    printf("set: %s\n", memcached_strerror(memc, rc));

    value = memcached_get(memc, "hoge", 4, &value_length, &flags, &rc);
    printf("get: %s\n", memcached_strerror(memc, rc));
    printf("test = %s\n", value);

    memcached_free(memc);
    return 0;
}
汚いソースを見たいという方はこの辺を...
Posted at by



2008/09/17


Google App Engineにファイルを転送しようとするとき、ファイルの数が多いと途中で失敗したり時間が掛かったりと結構嫌な思いをする事があったのですが、今日公開されたGAE SDK 1.3にてstaticファイルをZIPでまとめる事が出来る様になりました。
Google App Engine Blog: SDK 1.1.3 Now Available for download

Support for zipimport and a new module, zipserve, which serves static files from a zip archive. These allow you to work past the 1000-file app deployment limit.

The development console now includes a memcache viewer (you can use this by accessing http://localhost:8080/_ah/admin while your app is running on the SDK).

URLFetch now allows users to disable automatically following HTTP redirects.

We now allow composite indexes with repeated properties.

http://googleappengine.blogspot.com/2008/09/sdk-113-now-available-for-download.html
使い方も簡単。まず以下の様な構成のzipファイルを用意します。 Archive:  zippage.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
      289  08/09/17 13:03   index.html
      203  08/09/17 12:53   test.html
     4233  08/01/30 15:55   logo.png
 --------                   -------
     4725                   3 files
そしてアプリケーションハンドラの設定を以下の様に設定します。
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext import zipserve

def main():
  application = webapp.WSGIApplication(
    [
      ('/zippage/(.*)', zipserve.make_zip_handler('zippage.zip')),
    ])
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()
もちろんハンドラを分ければ、あるURLではpythonのハンドラ、あるURLではZIP内ファイルという事も出来ます。なお、動いている物はこの辺で確認出来ます。
他にも今回のバージョンアップにて開発者用コンソールにMemcache Viewerが使える様になりました。
gae-memcache-viewer
直接値を編集する事も出来ますが、現状マルチバイト文字でエラーが出ているようです。
他、URLFetchでURLの自動フォローを出来ないようになったり等の修正も行われています。

話は変わって、tokuhiromさん作のjavascript v8エンジンで動くCGIインタフェースllv8callですが、tokuhiromさんが入れ込んでる機能をwin32に移植して行っていますがlibmemcachedのwin32版がMSVCでビルド出来なかったので、自前でmemcacehdとお喋りする様にしました。興味がある方はこの辺のソースをご覧下さい。

Posted at by



2008/09/11


ちょっと訳あって、WScriptからjQueryを呼び出す必要があり(嘘です)作ってみました。

これを使うと $.each([1,2,3], function(index, item) {
    print("foo" + item);
});
$.ajax({
    type: "GET",
    url: "http://www.google.co.jp/",
    async: false,
    success: function(data) {
        print(data);
    }
});
こんなソースが実行出来ます。
$.eachなんかは目茶目茶便利なので、使わない手はありません。とりあえず、$.ajaxでgoogle.co.jpのソースが取得出来るくらいは動きます。
以下全体ソース。
// vim:fdm=marker fdl=0 fdc=0 fdo+=jump,search:
// vim:fdt=substitute(getline(v\:foldstart),'\\(.\*\\){\\{3}','\\1',''):
// {{{
(function(target) {
    target.window = {
        document : {
            defaultView : {}
        },
        navigator : {
            userAgent : "Windows Scripting Host"
        },
        location : {},
        XMLHttpRequest : function() {
            // copied from http://la.ma.la/misc/js/ie_xmlhttp.js
            var self = this;
            var props = "readyState,responseText,responseXML,status,statusText".split(",");
            this.readyState  = 0;
            this.__request__ = new ActiveXObject("Microsoft.XMLHTTP");
            this.__request__.onreadystatechange = function(){
                for(var i=0;i<props.length;i++){
                    try{
                        self[props[i]] = self.__request__[props[i]]
                    }catch(e){
                    }
                }
                self.onreadystatechange()
                if(self.readyState == 4) self.onload();
            }
            this.onreadystatechange = function(){};
        },
        setInterval : function(func, interval) {
            func(); // quickly f*ckin hack
        },
        clearInterval : function(timer) {
        },
        setTimeout : function(func, interval) {
            func(); // quickly f*ckin hack
        },
        clearTimeout : function(timer) {
        }
    };
    var methods = "open','abort','send','setRequestHeader','getResponseHeader','getAllResponseHeaders".split("','");
    var make_method = function(name){
        window.XMLHttpRequest.prototype[name] = function(){
            var params = new Array(arguments.length);
            for(var i=0;i<params.length;i++) params[i] = "_"+i;
            return Function(
                params.join(","),
                ["return this.__request__.",name,"(",params.join(","),")"].join("")
            ).apply(this,arguments);
        }
    };
    for (var i=0;i<methods.length;i++) make_method(methods[i]);
    for (var n in window) target[n] = window[n];
})(this);

function print(msg) {
    WScript.StdOut.WriteLine(String(msg));
}

function require(source, target) {
    target = target||this;
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    var stm = fso.OpenTextFile(source, 1, false, -2);
    var text = stm.ReadAll();
    stm.Close();
    eval(text, target);
    for (var n in windowif (typeof target[n] === 'undefined') target[n] = window[n];
}
// }}}

require("jquery-latest.js");

$.each([1,2,3], function(index, item) {
    print("foo" + item);
});

$.ajax({
    type: "GET",
    url: "http://www.google.co.jp/",
    async: false,
    success: function(data) {
        print(data);
    }
});
setTimeoutとかsetIntervalはf*ckingはhackなので$.get(非同期)は動きません。直す余地ありです。
あと、XMLHttpRequestのbindはmalaさんのをパクってます。
Posted at by




tokuhiromさん作のfcgi-v8をCGIインタプリタとして、cho45さん作のblosxom.rhinoを改造してblosxom.v8というのを作ってみた。
v8にはunevalが無かったのでdankogaiさん作のuneval.jsを使わせて頂いた。
実行画面はblosxom.rhinoの書き換えなので、パクリ気味ですが...
blosxom-v8
fcgi-v8でファイル日付の取得が実装出来ていないので、今は日付は表示されていません。
blosxom.v8
よかったら遊んでみて下さい。
Posted at by



2008/09/10


ひさびさPlagger弄り。
Lingua::JA::Galを使ってフィードテキストをギャル語に変換します。
注意する点はHTMLなフィードをプレーンテキストに置き換えてしまう点です。
これでPublish::MixiDiary使ってギャルなマイミク装ってみませんか。
コードはこの辺に。

Lingua::JA::Gal++
Posted at by



2008/09/05


shell.ccから改造したから分からなくなったんだよね。
v8::Objectに内部保持するnativeな値(例えばハンドルとか)を保持するには FILE* fp = fopen(...);
v8::Local<v8::Object> obj = v8::Object::New();
obj->Set(v8::String::New("value"), v8::External::New((void*)fp));
という様に、v8::Externalでメンバを追加してやれば良い。
ただ分からなくなってた原因としては、shell.ccが持ってるPrintという関数が v8::Handle<v8::Value> Print(const v8::Arguments& args) {
  bool first = true;
  for (int i = 0; i < args.Length(); i++) {
    v8::HandleScope handle_scope;
    if (first) {
      first = false;
    } else {
      printf(" ");
    }
    v8::String::AsciiValue str(args[i]);
    printf("%s", *str);
  }
  printf("\n");
  return v8::Undefined();
}
となってて、何でも文字列化してくれる様に思えてた。で実際 var obj = test();
print(obj.value); // ここでクラッシュ
こんな事やると落ちてしまうんだけど、良く考えたらExternalって言ってるんだから値の出しようもない。確かに以下の様なコードをshell.ccに含ませ global->Set(v8::String::New("hoge"), v8::External::New(NULL));
javascriptから"print(hoge)"で例外を発生させた際のデバッグ出力で確かめたら ==== Stack trace ============================================

(No security context)
    1: DefaultString(this=00C104BD <JS Object>#0#,x=00E0068D <Proxy>#1#)
(No security context)
    2: ToString(this=00C104BD <JS Object>#0#,x=00E0068D <Proxy>#1#)
    6: arguments adaptor frame: 1->0
Security context: 00E006A1 <FixedArray[41]>#2#
    7: /* anonymous */(this=00C10361 <JS Global Object>#3#)

==== Details ================================================

(No security context)
[1]: DefaultString(this=00C104BD <JS Object>#0#,x=00E0068D <Proxy>#1#) {
  // stack-allocated locals
  var v = 01000135 <undefined>
  var s = 01000135 <undefined>
  // expression stack (top to bottom)
  [04] : 01000361 <String[8]: toString>
  [03] : 00E0068D <Proxy>#1#
  [02] : 00E0068D <Proxy>#1#
--------- s o u r c e   c o d e ---------
function DefaultString(x) {?  if ((typeof(x.toString) === 'function')) {?    var s = x.toString();?    if (%IsPrimitive(s)) return s;?  }?  if ((typeof(x.valueOf) === 'function')) {?    var v = x.valueOf();?    if (%IsPrimitive(v)) return v;?  }?  throw %MakeTypeError('cannot_convert_to_primitive', []);?}
-----------------------------------------
}

(No security context)
[2]: ToString(this=00C104BD <JS Object>#0#,x=00E0068D <Proxy>#1#) {
  // expression stack (top to bottom)
  [02] : 01002845 <String[13]: DefaultString>
  [01] : 00C104BD <JS Object>#0#
  [00] : 0100232D <String[8]: ToString>
--------- s o u r c e   c o d e ---------
function ToString(x) {?  if ((typeof(x) === 'string')) return x;?  if ((typeof(x) === 'number')) return %NumberToString(x);?  if ((typeof(x) === 'boolean')) return x ? 'true' : 'false';?  if ((typeof(x) === 'undefined')) return 'undefined';?  return ((x === null)) ? 'null' : %ToString(%DefaultString(x));?}
-----------------------------------------
とNULL参照している事が分かる。v8からすると"訳分かんない物をToStringして表示しようとしただけさ。"と言ったところか。
つまりはv8::Externalの使い方が悪いわけではなく、v8::Externalを表示しようとしてたPrint関数がマズイのだ。さらに元々Externalなんか扱ってなかったshell.ccにExternalを埋め込んだ犯人が悪い。そう...私。
言ってしまえばExternal組み込んだんだから、Printもちゃんと修正しろよって事ですね。
if (argv[0]->IsExternal()) {
    printf("[native code]");
    continue;
}
よくブラウザなんかで見かける"[native code]"という表示になる様にしました。
この修正は「Google の JavaScript エンジン v8 で FastCGI する - TokuLog 改めB日記」での成果物"fcgi-v8"にも組み込んであります。
ただ、Extenalに保持する様な値というのは、ハンドルとかメモリ確保した領域だとか、状態を保持する様な物のはずなので、実際には確保したポインタ等のリストをグローバルな領域にを持っておき、終了時に解放するかPersistentなObjectTemplateでインスタンスを作り、WeakReferenceCallbackにて解放するのがベターという事になる。
Externalを使う側からすると、そこに何が入っててもおかしい訳でもなく、v8として取り決めもない(引数の型はvoidポインタ)。よって複数の種類を格納したい場合は種類も判別出来る物、例えば typedef enum _OBJECT_TYPE {
    OBJECT_TYPE_FILE_POINTER, // FILE*
    OBJECT_TYPE_WINDOW_HANDLE // ウィンドウハンドル
} OBJECT_TYPE;

typedef struct _OBJECT_VALUE {
    OBJECT_TYPE type;
    void* object;
} OBJECT_VALUE;

// 設定
OBJECT_VALUE* val = new OBJECT_VALUE;
val->type = OBJECT_TYPE_FILE_POINTER;
val->object = fopen("test.dat", "rb");
obj->Set(v8::String::New("value"), v8::External::New((void*)val));

// 取得
v8::Local<v8::Value> valueobj = obj->Get(v8::String::New("value"));
if (!valueobj->IsExternal()) return v8::ThrowException(v8::String::New("Exception: invalid object"));
OBJECT_VALUE* val = static_cast< OBJECT_VALUE* >(v8::Handle<v8::External>::Cast(valueobj)->Value());
if (val->type == OBJECT_TYPE_FILE_POINTER) {
    // FILE*な処理
} else
if (val->type == OBJECT_TYPE_WINDOW_HANDLE) {
    // ウィンドウハンドルな処理
}
こんな構造と設定および取得をすべき、という事になる。ここまで気付くのに結構時間を使ってしまった。

さてこのPrint関数ですが、↑のリンクにある"fcgi-v8"には「Big Sky :: Google Chromeが使っているjavascript v8エンジンにTwitterにアクセス出来るクラスを作ってポストする。」でご紹介したtwitter-v8とは違う方法でPrintを実装してあります。
twitter-v8の方はコンソールに出力する物なのでsetlocaleとワイドキャラクタ出力(%S)でOKですが、fcgi-v8の場合はUCSからUTF-8への変換を組み込んであります。一応v8のプロジェクトサイトでIssue 17 : 「Add Strign::WriteUtf8」としてリクエストされているので、もしかすると今後このコードは必要なくなるかもしれませんね。
v8エンジンは高速な上、今まで私が見た中で一番組み込みやすい代物になっているかと思います。皆さんもv8使った面白いもの作ってみませんか。

twitter-v8、fcgi-v8それぞれ参加者をお待ちしております。
Posted at by



2008/09/03


v8エンジン盛り上がってますね。
さて、今日はv8エンジンのshell.ccを弄って、シェル内部にTwitterオブジェクトを作ってみました。とはいっても、仕組みは簡単。FunctionTemplateから得たPrototypeTemplateにメソッドを追加、さらにInstanceTemplateにプロパティusernameとpasswordを足しているだけです。
  v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
  v8::Local<v8::Template> p = t->PrototypeTemplate();
  p->Set("friendsTimeline", v8::FunctionTemplate::New(TwitterFriendsTimeline));
  p->Set("updateStatus", v8::FunctionTemplate::New(TwitterUpdateStatus));
  v8::Local<v8::ObjectTemplate> i = t->InstanceTemplate();
  i->Set(v8::String::New("username"), v8::String::New(""));
  i->Set(v8::String::New("password"), v8::String::New(""));

  global->Set(v8::String::New("Twitter"), t);
このTwitterFriendsTimelineには以下の様なコードを...
v8::Handle<v8::Value> TwitterFriendsTimeline(const v8::Arguments& args) {
  v8::Handle<v8::Value> ret = v8::Undefined();
  v8::Local<v8::Object> This = args.This();
  v8::HandleScope handle_scope;
  v8::TryCatch try_catch;

  v8::String::AsciiValue username(This->Get(v8::String::New("username")));
  v8::String::AsciiValue password(This->Get(v8::String::New("password")));

  CURL* curl = NULL;
  CURLcode res = CURLE_OK;
  char *auth;

  response_data = NULL;
  response_size = 0;
  curl = curl_easy_init();
  if (!curl) return v8::ThrowException(v8::String::New("Error: unknown"));

  int n = strlen(*username) + strlen(*password);
  auth = (char*) malloc(n + 2);
  memset(auth, 0, n + 2);
  strcpy(auth, *username);
  strcat(auth, ":");
  strcat(auth, *password);

  curl_easy_setopt(curl, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.json");
  curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  free(auth);

  if (res == CURLE_OK) {
    char* json = NULL;
    json = (char*) malloc(response_size+1);
    memset(json, 0, response_size+1);
    memcpy(json, (char*) response_data, response_size);
    v8::Handle<v8::String> source = v8::String::New(json);
    free(json);

    v8::Handle<v8::Script> script = v8::Script::Compile(source);
    if (script.IsEmpty()) {
        ret = v8::ThrowException(v8::String::New("Error: unknown"));
        goto leave;
    }
    v8::Handle<v8::Value> result = script->Run();
    if (script.IsEmpty()) {
        ret = v8::ThrowException(v8::String::New("Error: unknown"));
        goto leave;
    }
    if (!result->IsArray()) {
        ret = v8::ThrowException(v8::String::New("Error: unknown"));
        goto leave;
    }
    ret = result;
  }

leave:
  if (response_data) free(response_data);
  response_data = NULL;
  return ret;
}
さらにTwitterUpdateStatusには v8::Handle<v8::Value> TwitterUpdateStatus(const v8::Arguments& args) {
  v8::Handle<v8::Value> ret = v8::Undefined();
  v8::Local<v8::Object> This = args.This();
  v8::HandleScope handle_scope;
  v8::TryCatch try_catch;

  if (args.Length() != 1) return v8::ThrowException(v8::String::New("Error: usage(message)"));

  v8::String::AsciiValue username(This->Get(v8::String::New("username")));
  v8::String::AsciiValue password(This->Get(v8::String::New("password")));

  v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
  v8::Local<v8::Function> func = v8::Function::Cast(*global->Get(v8::String::New("encodeURIComponent")));
  v8::Local<v8::Value> funcargs[1];
  funcargs[0] = args[0]->ToString();
  v8::Local<v8::Value> result_val = func->Call(global, 1, funcargs);
  v8::String::AsciiValue escaped(result_val->ToString());

  char* status = url_encode_alloc(*escaped);

  CURL* curl = NULL;
  CURLcode res = CURLE_OK;
  char *auth;
  char url[2048];

  response_data = NULL;
  response_size = 0;
  curl = curl_easy_init();
  if (!curl) return v8::ThrowException(v8::String::New("Error: unknown"));

  memset(url, 0, sizeof(url));
  strncpy(url, "http://twitter.com/statuses/update.xml", sizeof(url)-1);
  strncat(url, "?status=", sizeof(url)-1);;
  strncat(url, status, sizeof(url)-1);
  free(status);

  int n = strlen(*username) + strlen(*password);
  auth = (char*) malloc(n + 2);
  memset(auth, 0, n + 2);
  strcpy(auth, *username);
  strcat(auth, ":");
  strcat(auth, *password);

  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
  curl_easy_setopt(curl, CURLOPT_POST, 1);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  free(auth);

  if (res != CURLE_OK) {
    ret = v8::ThrowException(v8::String::New("Error: unknown"));
  }
  if (response_data) free(response_data);
  response_data = NULL;
  return ret;
}
これだけ準備出来れば、あとは var twitter = new Twitter()
twitter.username = "my-username"
twitter.password = "my-password"
var statuses = twitter.friendsTimeline();
for (var n = 0; n < statuses.length; n++) {
    print(statuses[n].user.screen_name, ":", statuses[n].text);
}
twitter.updateStatus("v8エンジン12気筒");
こんなスクリプト書いて # shell twitter.js とかやればfollowerのステータスが出た後でメッセージがポストされます。
簡単ですね。
ちなみに既存のPrintではAsciiValueを使いますが、AsciiValueではUTF-8が8bit落ちするのでsetlocaleを使ってワイドキャラでprintfする様修正しています。
コードはcodereposのこの辺に置いときます。汚いソースですが、よろしければ参考まで...
Posted at by




Tumblr - Post to Tumblr : vim online

Script that allows you to post regular type posts to Tumblr.

といっても3行
*** tumblr.vim.orig Wed Sep 03 14:49:50 2008
--- tumblr.vim  Wed Sep 03 14:47:15 2008
***************
*** 50,55 ****
--- 50,58 ----
      url = "http://www.tumblr.com/api/write"
      email = vim.eval("g:tumblr_email")
      password = vim.eval("g:tumblr_password")
+     enc = vim.eval('&encoding')
+     title = title.decode(enc).encode('utf-8')
+     body = body.decode(enc).encode('utf-8')
      data = urllib.urlencode({"email" : email, "password" : password, "title" : title, "body" : body})
      res = urllib.urlopen(url, data)
  

このパッチを当てたtumblr.vimをpluginフォルダに置き
let g:tumblr_email='your@example.com'
let g:tumblr_password='tumblr-password'
と設定し新しいバッファ :TumblrNew
とすると Title:
-- Post follows this line --
と表示されるので、Title部と破線以下に入力し、 :TumblrPost
と実行します。一応rangeにも対応出来ているらしいです。
Textしかポスト出来ませんが、改造すればQuoteも行けるんじゃないですかね。ただvimで編集中のテキストが引用元になるから何がQuoteか分かりませんが...
Posted at by




scons入れてwin32でビルドしてみたよ。ベンチマークも取ってみた。
環境はWindows XP、P4 3GHz CPU、1G memで。

まずmozillaのjsエンジン(ちょっと昔)
JavaScript-C 1.7.0 2007-10-03 (js.exe)
Richards: 87
DeltaBlue: 84
Crypto: 44
RayTrace: 78
EarleyBoyer: 87
----
Score: 74

次にrhino
Rhino 1.6 release 7 (win32 nativeビルド版)
Richards: 6
DeltaBlue: 5
Crypto: 4
RayTrace: 11
EarleyBoyer: 12
----
Score: 7

Rhino 1.7 release 7 2008 03 06(java版 js-14.jar)
Richards: 30
DeltaBlue: 30
Crypto: 24
RayTrace: 47
EarleyBoyer: 53
----
Score: 35

そしてv8エンジン
V8 version 0.2.5 (shell.exe)
Richards: 1020
DeltaBlue: 981
Crypto: 622
RayTrace: 580
EarleyBoyer: 1070
----
Score: 827
はぇーーーーーーーーーーーーーーー!(゚д゚;)

ベンチマークはbenckmarksフォルダにあるrun.jsで起動します。

追記
取り直したらこんな結果もでた
Richards: 1391
DeltaBlue: 1224
Crypto: 940
RayTrace: 774
EarleyBoyer: 1360
----
Score: 1110
もっと詳細が知りたい方は以下を参照。
Google V8 についてしらべてみました - TokuLog 改めB日記
Posted at by



2008/09/02


追記
teramakoさんがもっと良いの 作ってくれました。
きっとそっち使う方が幸せな、ともだちんこになれます。
ですので、私のはubiquityNeta.jsにリネームする事にします。


こう使うのですね?わかります。
(function() {
    if (typeof gUbiquity === 'undefined'return;
    liberator.commands.addUserCommand(["ubiquity"], "Ubiquity",
        function(arg, special){
            var anchor = document.getElementById("content");
            if (window.location == "chrome://browser/content/browser.xul")
                anchor = anchor.selectedBrowser;
            gUbiquity.openWindow(anchor);
            gUbiquity.__textBox.value = arg||'';
            var context = gUbiquity.__makeContext();
            var previewBlock = document.getElementById("cmd-preview").contentDocument.getElementById("preview");
            gUbiquity.__cmdManager.updateInput(arg||'', context, previewBlock);
            if (special) {
                gUbiquity.__cmdManager.execute(context);
                gUbiquity.closeWindow();
            }
        },
    { });
})();
使い方は
:ubiquity map osaka
でプレビュー表示
:ubiquity! map osaka
で直実行です。
ubiquity-vimperator
ソースはcodereposに置いておきます。
ubiquityNeta.js
ubiquityとvimperatorは、ともだちんこですね。わかります。

参考:
ゆびきちが相当面白い!! - ネットランダム

指基地なのか、指既知なのかは置いておいて、ユビキタス度アップ間違いなしのFirefoxアドオンである。

今後、Greasemonkeyにとって変わる可能性すらある。

っていうか、Vimperatorユーザ涙目!

http://d.hatena.ne.jp/fk_2000/20080901/p2
Posted at by