2012年

結局、またぜんぜん記事書いてないですな…。

でも、まあ、一応生きてはおります。

なにより、ブログのパスワードがわかんなくなっちゃって、調べるのがめんどくさくてついついというのが大きかったですが、ようやくわかりました。

が、だからといって、そうそう更新することもないのだろうな、と思う年明けです。

2011日本シリーズ終了

ソフトバンクの4勝3敗で決着しましたが、ホークスとそのファンの方々おめでとうございます。

パ・リーグを圧倒的な強さで勝ち抜いたホークスに対して、7戦まで粘ったドラゴンズもなかなかよくやったんじゃないかと思います。

思えばドラゴンズのような最低打率のチームが優勝するという、統一球採用による投高打低のシーズンを見事に反映したシリーズでした。
ドラゴンズは日本シリーズ史上最低の打率(.155)、最少得点(9点)、最小本塁打2などなど、数々の最低打撃記録を打ち立てながら、7試合を戦い抜きました。

そもそもドラゴンズの打線は少々工夫したところで爆発する要素などなく、シーズン中同様、細かくチャンスを作りながら確率論的に時々得点するというきわめて地味な作業を淡々と行う、まるで昔の役モノ台のような打線であったわけで、小手先の手当を全く行うことなく最終戦まで平常通りの対応で臨んだ落合監督の戦略は基本的に正しかったのだと思います。

投手陣も基本的に大きな破綻はなく、被弾も想定内の大きさで抑えきったといえるでしょう。第5戦で5失点したのが最大の失点でしたが、チェンはこのシリーズで1勝1敗であることを考えると、この敗戦もいわゆる「想定内」であったといえます。

結果から見れば、あのとき誰々を起用していれば…、みたいな論議は可能かと思いますが、実際にそのようなことをやって平常通りのドラゴンズでない戦いをやったところで4戦を先勝というのはかなり困難であったと思われます。先のことを考えずに手持ちの戦力をとにかく投入していくというCSのスワローズのような戦い方ではどう考えても破綻します。

このシリーズの一番のポイントといえば、誰もがおそらく第4戦のノーアウト満塁から森福に抑えこまれたシーンを思い浮かべるでしょう。しかし、あのようなことはシーズン中から数限りなくあったのです。どちらかと言うと、あのようなチャンスをとにかく何度も作りつつ、その何分の一かの機会に得点をするというのが今シーズンの中日の戦い方でした。

そこには、いわゆる打線のつながりとか、打線の勢いとかを重視するような攻撃のイメージはありません。むしろ個々の選手が可能なかぎりの実力を出しつつ、それらがうまく組み合わさったときには自ずと得点が入るという感じです。調子に乗せたらどうしようもない、とか、打ち出したら止まらない…的な打線から一番遠いところにいるイメージです。

ところが、9月後半から10月にかけて、どういうわけか打線に勢いがついてしまった。また、CSからシリーズの1,2戦にかけていわゆる「一発」で決める試合が続いてしまったことで、そもそも基本としていた貧打で地道に攻めるという基本的な攻撃姿勢が崩れた感じがしました。あるいは短期決戦ということを意識しすぎて、打者の意識がシーズン中と違い、一気に決めてしまいたいという方向に傾いてしまっていたのかもしれません。

特に最終戦では、いわゆる打線が粘りを見せる、というシーンがほとんど見られませんでした。それは杉内の投球がそれを許さない内容であったのか、それともドラゴンズの各打者が通常の打席よりも敵投手に食らいついていく意識が低くなってしまっていたのか、そこまではよくわかりませんが、結果として明らかに淡白な内容であったのは否定できません。

負け惜しみではありますが、第7戦まで持ち込んだということは、ホークスと実力伯仲で五分の戦いができたということは間違いないと思います。
落合監督が就任してから、その基本路線通りに大型補強を行わず、いわゆる現有戦力で勝てるチームにする、という目標は8年間連続Aクラス入りということでその成果が十分に示されています。そして日本シリーズでも十分に通用するチームになったと言えると思います。

ただ、そのチームは落合という監督がいてはじめて機能するチームともいえるわけで、このオフではたしてどのような変化が起こるのかに興味を持って注視していきたいと思います。

Lー04CとATOK

QWERTYキーボード付きのL-04CにATOKを入れて使ってたんだけど、先日のATOKのアップデートによってついにL-04Cの「文字」キーに対応してくれるようになりました!

これまで変換のしやすさからATOKを使っていたのだけど、入力文字を日本語から英文字に切り替えるときのキーバインドがちゃんと対応してなくて、Fn + Space, Fn, Fn とか3回もキーを押さなきゃならなかったわけです。
もうその動作になれてきちゃって、それはそれでいいかと半ばあきらめておったのですがね。

でも、やはり1キーで切り替えができるというのは、非常にありがたいということを、この文章を書きながらも実感している次第であります。

ありがとう、ATOKさん。

FMV-645NU6C/Lのメモリを増強してみた。

image

もう5年か6年も前に中古屋でジャンクで手に入れたFMV-645NU6C/Lというのを、寝る前に寝床でゴロゴロしながらいじったりするのに使っている。
メモリは128MBを2枚で256MBにしてUbuntu10.04をインストールしてあり、まあ、かなり鈍重な動作ではあるものの、のんびりブラウザを眺める程度の目的ならそれなりに役にたってくれている。登場したのが2000年ということで、まさに「前世紀の遺物」なのだけど「遅い」という点を除けば、液晶もキーボードもCDも壊れることなく立派に動いてるというなかなかタフなやつ。

富士通のサイトで販売されてた頃のスペックを見るとメモリの上限は256MB。ひょっとしたら両面実装のメモリならもうちょっと増やせるかもなぁ、と思いながらも両面実装256MBのメモリもそんなに安くない(下手するとジャンクで購入した本体価格を上回る)ので、もう何年もこれ以上の増強は見送ってきた。

で、先日のこと。ふと覗いた店でジャンクメモリばかりを集めた箱の中になんと両面実装の256MBが。
ダメ元で1枚買って、すでに入れてあったメモリと入れ替えてみたところ、幸いなことにしっかり認識され、128+128=256しかなかったメモリが128+256=384MBと増強されて、スワップしまくりだったubuntuの動作があきらかに滑らかになった。

とはいえ、まだ日本語の変換のときに一呼吸できてしまうくらいの時間がかかってしまうこともあり、滑らかになったとはいえあくまで「それなり」の速度でしかないのだけれども。でも、こんなことならもっとジャンクの箱の中を探してもう一個買っておくのだったと、やや後悔するぐらいにはなってるわけで。ケチな話で、動かなかったら無駄になるし、と思ってちゃんと探さなかったんだよねぇ。

CodeIgniterでTwitterに画像を投稿するフォームを作ってみた

先日Twitterに画像を投稿する機能が追加され、それを利用するためのAPIも公開された。

で、その機能を使ってCodeIgniterで画像をアップするためのフォームを試しに作ってみました。

作ったと言っても、上記APIページからリンクされているPHPのライブラリであるtmhOAuthというのを拝借して、OAuthとupdateを組み込んだだけ。

また、画像のアップロードに関しては、「CodeIgniter ユーザガイド 日本語版」のファイルアップロードクラスのサンプルを流用しました。

まず、tmhOAuthのライブラリをダウンロードしてきて、CodeIgniter(CI)のsystemディレクトリ以下にtmhOAuthというディレクトリを作ってその中に入れます。

/system/tmhOAuth/tmhOAuth.php
/system/tmhOAuth/tmhUtilities.php

という感じ。

つぎにtmhOAuthについているexamplesのなかから、photo_tweet.phpとかoauth_flow.phpとかを参考にして、自前のtwitterライブラリを作りました。tmhOAuthのラッパーという感じです。

/application/libraries/twitter.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class CI_twitter {

  public function __construct($config = array()){

    if (count($config) > 0){
      $this->initialize($config);
    }

    log_message('debug', "Twitter Class Initialized");

  }

  public function initialize($config = array()){

    $defaults = array(
       'tmhOAuth_dir'    => BASEPATH.'tmhOAuth',
       'consumer_key'    => '',
       'consumer_secret' => ''
    );

    foreach ($defaults as $key => $val){
      if (isset($config[$key])){
        $this->$key = $config[$key];
      }else{
        $this->$key = $val;
      }
    }

    require $this->tmhOAuth_dir.'/tmhOAuth.php'
    $this->to= new tmhOAuth(
      array(
        'consumer_key'    => $this->consumer_key,
        'consumer_secret' => $this->consumer_secret
      )
    );

  }

  function request_token($callback=''){

    $code = $this->to->request(
      'POST',
      $this->to->url('oauth/request_token', ''),
      array(
        'oauth_callback' => $callback
      )
    );

    return $code;

  }

  function authorize(){

    $CI =& get_instance();
    $oauth=$this->to->extract_params($this->to->response['response']);
    $CI->session->set_userdata('oauth',$oauth);
    $authurl=$this->to->url("oauth/authorize", '')."?oauth_token={$oauth['oauth_token']}";
    header("Location: {$authurl}");

  }

  function access_token($user_token,$user_secret){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
      $this->to->url('oauth/access_token', ''),
      array(
        'oauth_verifier' => $_REQUEST['oauth_verifier']
      )
    );

    if($code==200){
      $access_token=$this->to->extract_params($this->to->response['response']);
    }else{
      $access_token=NULL;
    }

    return $access_token;

  }

  function update_with_media($user_token,$user_secret,$image,$status){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
      'https://upload.twitter.com/1/statuses/update_with_media.json',
      array(
        'media[]'  => "@{$image}",
        'status'   => $status
      ),
      true, // use auth
      true  // multipart
    );

    return $code;

  }

  function update($user_token,$user_secret,$status){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
       $this->to->url('1/statuses/update'),
      array(
        'status'   => $status
      )
    );

    return $code;

  }

  function response(){
    return $this->to->response['response'];
  }

}

今回使おうとしてるOAuth関連と画像つきupdate関連しか実装してませんが、元のtmhOAuthにはいろいろ他にも機能があるので、必要なものを足していけばいいのかなー、と思います。

で、そのつぎに、コントローラをファイルアップロードクラスのサンプルのものをごにょごにょいじって作ります。

/application/controller/welcome.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class Welcome extends CI_Controller {
  function __construct(){
    parent::__construct();
    $this->load->library(array('session','form_validation'));

    $this->load->library(
      'twitter',
      array(
        'consumer_key'    => $this->config->item('consumer_key'),
        'consumer_secret' => $this->config->item('consumer_secret')
      )
    );

    $this->load->helper(array('form', 'url'));

//    $this->output->enable_profiler(TRUE);

  }

  function index(){

    $data['status']='';
    $data['error'] ='';

    $this->load->view('form', $data);

  }

  function do_upload(){

    $config['upload_path']  ='<ファイルアップロード用のパス>/uploads/';
    $config['allowed_types']='gif|jpg|png';
    $config['max_size']     ='1000';
    $config['max_width']    ='1024';
    $config['max_height']   ='768';
    $config['encrypt_name'] =TRUE;

    $this->load->library('upload', $config);

    $this->_set_validation();
    $info=$this->_bind_data();

    $data=array();
    $data['info']=$info;

    if(!$this->upload->do_upload() || !$this->form_validation->run()){

      $data['error']=$this->upload->display_errors();

      $this->load->view('form', $data);

    }else{

      if($this->session->userdata('access_token')){

        $access_token=$this->session->userdata('access_token');
        $upload_data=$this->upload->data();

        $this->_update_status(
          $access_token,
          $upload_data,
          $data
        );

      }else{

        $this->session->set_userdata('info',$info);
        $this->session->set_userdata('upload_data',$this->upload->data());

        $code=$this->twitter->request_token(// callback url
          $this->config->item('base_url').'welcome/access_token/'
        );

        if ($code == 200) {
          $this->twitter->authorize();
        } else {
          echo 'There was an error: ' . $this->twitter->response() . PHP_EOL;
        }

      }
    }

  }

  function access_token(){
    $oauth=$this->session->userdata('oauth');

    $access_token=$this->twitter->access_token($oauth['oauth_token'],$oauth['oauth_token_secret']);

    if ($access_token) {
      $this->session->set_userdata('access_token',$access_token);
      $this->session->unset_userdata('oauth');

      $data['info']=$this->session->userdata('info');
      $upload_data=$this->session->userdata('upload_data');

      $this->_update_status(
        $access_token,
        $upload_data,
        $data
      );

    } else {
      echo 'There was an error: ' . $this->twitter->response() . PHP_EOL;
    }
  }

  function sess_clear(){

    $this->session->sess_destroy();
    redirect('');

  }

  function _update_status($access_token,$upload_data,$data){

    $data['image_dir']='<ドキュメントルートからのパス>/uploads/';

    $code = $this->twitter->update_with_media(
      $access_token['oauth_token'],
      $access_token['oauth_token_secret'],
      $upload_data['full_path'],
      $data['info']['status']
    );

    if ($code == 200) {
      $data['upload_data']=$upload_data;
    }else{
      $data['upload_data']=array();
      $data['info']['status']=$this->twitter->response();
    }

    unlink($upload_data['full_path']);

    $this->load->view('success', $data);

  }

  function _set_validation(){

    $validate_rules=array(
      array(
        'field' => 'status',
        'label' => 'status',
        'rules' => 'required'
      )
    );

    $this->form_validation->set_rules($validate_rules);

  }

  function _bind_data(){

    $params=array(
      'status'
    );
    foreach($params as $val){
      $data[$val]=$this->input->post($val);
    }

    return $data;
  }

}

コンストラクタ内でさっきのtwitter.phpライブラリを呼んでいます。引数はconsumer_keyとconsumer_secretです。これらの値はtwitterのアプリを作成したときのものをconfig.phpのなかに追加してあります。
おもなメソッドはindexとdo_uploadとaccess_tokenです。do_uploadが呼ばれて、OAuthのaccess_tokenがない場合、OAuthの手続きに入ります。

その際に$this->twitter->request_tokenの引数にコールバックするURLを入れるようにしています。この場合はaccess_tokenメソッドを呼ぶようにしています。access_tokenを呼ぶときにさらに引数を足してaccess_token内で処理を分けるようにすることもできると思います。

その他の_set_validationとか_bind_dataとかはフォームからサブミットされた値を取るときの僕のいつもの流儀なので、このへんの処理は好きにやって下さい。

あとはビューを2つ。フォームと送信後の完了画面です。

/application/views/form.php

<html>
<head>
<title>アップロードフォーム</title>
</head>
<body>

<?php echo $error;?>
<?php echo validation_errors();?>

<form action="/photoup/welcome/do_upload" method="POST" enctype="multipart/form-data">

<input type="file" name="userfile" size="20" /><br>

status <input type="text" name="status" size="100">

<br /><br />

<input type="submit" value="upload" />

</form>
<p><a href="/photoup/welcome/sess_clear">clear session</a></p>
</body>
</html>

/application/views/success.php

<html>
<head>
<title>アップロードフォーム</title>
</head>
<body>

<h3>成功!</h3>

<ul>
<?php foreach ($upload_data as $item => $value):?>
<li><?php echo $item;?>: <?php echo $value;?></li>
<?php endforeach; ?>
</ul>
<p><strong><?=form_prep($info['status'])?></strong></p>

<p><?php echo anchor('', '次の画像をアップする!'); ?></p>

</body>
</html>

こんな感じです。ユーザガイドのサンプルのほぼパクリです。

CodeIgniterのwikiにOAuthのライブラリとかも紹介されているんですが、画像つきのアップロードとかやるときにちょっと不便かなー、と思ってtwitterのAPIのページからリンクされてたライブラリを直に使ってしまいました。あんまりCI的には美しい実装とは言えないかもしれません…。

(追記:2011/8/19)
twitter.phpのライブラリの設定の仕方がおかしかったので修正しました。

IS01で、すこし未来に近づいてみた

国産初のAndroid端末でありながら、キャリアからアップデートの見送りを早々に発表されてすっかり流れに取り残されてしまったIS01。

有志の方々による地道なアップデートへの努力があるのは知っていたけど、今までの方法はいずれもかなりの熟達者向けで、おまけにひとつ間違うと二度と動かなくなってしまう『文鎮化』のリスクがあるため、なかなか手を出せるものではなかった。

ところが、この7月にはいって伝わってきた方法は、必要なスキルも、所謂『文鎮化』リスクも低く、さらに元の環境も一切傷つけないため、いつでも元の1.6の環境に戻せるという、かなりとっつきやすい方法だった。

具体的な方法は、こちらのブログに詳しい。
is01でFroyo(android1.6とデュアルブート) – is01next@blog

できあがったFroyo環境はとても安定しているし、動作速度もほとんど問題ないレベルだった。あたらしいアプリが使えないことでIS01を使わなくなった人は試して見る価値はあると思う。(3Gは使えないけど!)

ただ、僕のこれまでのIS01でどうしても外せない『WiFiのad-hoc接続』(これでwillcom03のwifisnapに接続する)ができない、という点がひっかかるため、個人的には残念ながらFroyo環境は常用できない感じだ。

それと、先日機種変して実質0円で手に入れたDell Streakで新しいアプリは動かすことができるし、テキスト入力を多く使うようなIS01のキーボードを活かすアプリはそれほど新しくなくてもある程度揃っているので、現時点ではあえてワンセグやad-hocを捨ててFroyoに行く理由がない感じというか…。

でも、なんとなく、ad-hocもワンセグも、解決されるのは時間の問題のような気もするので、ひきつづきこの動きはおっていきたいと思うのでした。

Androidで「エラーが発生しました…」と言われてtwitterにログインできない件

このことで、昨日夜から、目が覚めて先ほどまでずっと悩まされてた。

機種はDell StreakでAndroidは2.2。
設定の「アカウントと同期」から「アカウントを追加」でtwitterのアカウントを追加しようとするのだけど、何度やってもアカウントとパスワードを入力するフォームの画面から先に進めなかった。

WEBブラウザからtwitter.comにアクセスする場合はちゃんとログインできるので、アカウントとパスワードが間違っている、という単純ミスではないし、Android1.6のIS01からなら代表的なtwitterアプリであるtwiccaでちゃんとログイン出来ている。

原因は、時刻の設定だった。
何らかの原因で(あるいは最初から?)タイムゾーンが世界標準時(つまりロンドン)になっていた。
これを東京に変更して正しい日付と時刻に修正することで解決。

気づかせてくれたのは、こちらのページだった。

TwitterのアプリがプリインストールされているAndroidの場合、あとからインストールするTwitterのアプリもプリインストールされているTwitterアプリの認証の仕組みを利用するものが多く、PINコードを取るにしてもプリインストールされてるTwitterアプリが認証されていないことには先に進めない。
また、直接Twitterに認証を得ようとするアプリも時刻が狂っていると認証を取得できないようだ。

検索するとおなじようなログイン出来ないお悩みが結構見つかるのだが、みなさん解決されたのだろうか…。

willcom03でふつうにFacebookにアクセスする

以前の記事でwillcom03でwillcomの回線を使ってfacebookにアクセスすると認証の際に端末の個体識別IDの送出を求められるが、willcom03のブラウザはそのような仕様になっていないため認証をパスすることができないということを書いた。

それからずっとwillcom03からブラウザを使ってfacebookにアクセスすることはなかったのだけど、昨夜ふと試してみたところ、すんなりと認証をパスすることができた。

おそらく端末の個体識別IDに紐づけた認証をしない方法に改められたということだと思われるのだけど、まだ詳しくは見てないのでcookieを使ってるのかとか、端末IDを送出する端末での認証方法も変わったのかとかはわからない。

とりあえず、willcom03からwillcom回線経由でブラウザをつかってfacebookにアクセスすることはできるようになっているということで。

MeeGoでアプリをインストールしてみる

EeePC900AにMeeGo1.2をインストールしてしばらく経つが、このPCでやりたことのほとんどがChromeで済んでしまうので、デフォルトでインストールされているアプリケーション以外にインストールしたのは日本語変換で使うscim-anthyだけだった。

ふと、Twitterのクライアントでもあると便利かな?と思って検索してみたところ、QWassrというアプリがあるのを知った。で、さらに検索してみるとQWassrの作者さんが丁寧にMeeGoのアプリマーケットであるAppUpのインストール方法などを解説されているページが見つかった。
QWassr for MeeGo を Intel AppUp で公開したよ

まだAppUp自体が開発者向きというステータスらしいので、ちょっとインストールは面倒くさいがとくに難しい点はなかった。

AppUpをインストールしたのち、「twitter」などで検索してQWassrをインストールしてみた。…が、なぜか最初、2回目とAppUpのエラーで途中で終わってしまい、ちゃんとインストールできない…。

インストールできなかった理由は不明だが、ダメ元で3回めに試すと何事もなかったように最後まで処理が進み、無事インストールが完了した。

AppUp

AppUpの画面

で、さっそくQWassrを使ってみたところ、こんどはなぜかつぶやきの入力フォームでscimが起動しない…。

うーん、まだまだ前途多難な雰囲気に満ちているなぁ。ibusとかだったら大丈夫なんだろうか…。

DroppedBoxxがバージョンアップしていた

4月に有料版のDroppedBoxxが1.6にバージョンアップしていた。

しかしあいかわらず日本語マルチバイトファイル名のファイルが同期する時にアップロードされないのは直っていないようだ。

作者のTwitterによると、Marketplaceの6.x対応版はもうすぐ閉じられてしまうので次の1.7が最後のバージョンとなるだろうとのこと。
マルチバイトファイル名問題は解決しないのかもね…。