nDiki : エラー処理

エラー処理 - error handling

以下「呼び出されるもの」をメソッドと書くこととする。サブルーチンも関数と呼んでもほぼ同じ。

エラーのチェック

実行が失敗する可能性のあるメソッドの呼び出し側は、メソッド呼び出し後に成功したか失敗したかをチェックして、失敗だった場合適切な処理を行う必要がある。

エラー状態の取得

メソッドの実行が成功か失敗かどうかの情報がどのように得らるようになっているかは、メソッドによって様々である。

メソッドの返り値によって成功・失敗が返されるタイプ

成功したかどうかが true か falseか、あるいは 0 か !0 などで返されるタイプ。

  if (obj.run()) {
    // success
  }

失敗の場合はエラー番号などが返されるものがある。真偽値として評価できる値を返す場合はそのまま条件式などで使用できる。

エラー状態受け渡しのオブジェクトを呼び出し時に引数として指定するタイプもある。

成功した場合は結果の値が、失敗した場合は特殊な値が返るタイプ

失敗した場合に null を返すものや、通常0以上の値を返す時に失敗の場合は負の値を返すものなど。

  Data result = obj.run();
  if (result != null) {
    // success
  }

チェックを忘れると、誤った値で計算を続けてしまったりヌルポインタ例外を引き起したりする。

結果受け渡しオブジェクトを呼び出し時に指定するタイプもある。

メソッドの呼出し後、状態受け渡し用オブジェクト(やグローバル変数など)などに状態がセットされるもの

  obj.run();
  if (globalStatusObject.isSuccess()) {
    // success
  }

メソッドの呼だし後、状態確認用メソッドなどを呼んで状態を取得するもの。

  obj.run();
  if (obj.isSuccess()) {
    // success
  }

失敗の場合例外を発生するもの

  try {
    obj.run();
  }
  catch (Exception x) {
    // error
  }

どこかで捕捉する必要がある。

何も教えてくれないもの

困る。

Null Object パターン

Bobby Woolf による。

null のかわりに Null Object を使用して null チェックを不要にするパターン。

2005年4月2日 (土)

DAR で差分/増分バックアップ

普段使っているノート PCpdumpfsバックアップをとっている。 任意のスナップショットから簡単にファイルを復元できるので、バックアップHDDを別に用意できる場合はこれが便利。

問題1

会社で使っている Windows デスクトップは、rsyncWindowsファイルサーバへ同期。 1世代しかバックアップが無い。 少なくとも数世代前のファイルが復元できるようにしておきたい。

問題2

Linux サーバはバックアップ無し! マズイ。 現状、たまに手動で tarball にして保存しているぐらい。

DAR

DAR というバックアップコマンドの紹介を見て興味をひかれた。 シンプルながらも使い勝手の良さそう。 Linux でも Windows でも動くというのも嬉しい。

DAR を使ってみる

Linux 上で試してみる。

テスト用ディレクトリを作成

/tmp の下にテスト用ディレクトリ dar を作成。 その下に home ディレクトリと var ディレクトリを作成する。

 mkdir -p /tmp/dar/home/naney
 mkdir -p /tmp/dar/var/lib/dar
 echo 'abc' > /tmp/dar/home/naney/file1.txt

/tmp/dar/home 以下バックアップ対象として /tmp/dar/var/lib/dar 以下にバックアップファイルを作成してみることにする。

フルバックアップ

最初はフルバックアップ:

 dar -c /tmp/dar/var/lib/dar/home-full \
     -y9 \
     -R /tmp/dar \
     home

/tmp/dar/home をフルバックアップした home-full.1.1.dar が /tmp/dar/var/lib/dar にできる。

差分バックアップ(1回目)

ファイルを1つ追加。

 echo 'def' > /tmp/dar/home/naney/file2.txt

ここで差分バックアップをとる:

 dar -c /tmp/dar/var/lib/dar/home-diff-1 \
     -A /tmp/dar/var/lib/dar/home-full \
     -y9 \
     -R /tmp/dar \
     home

home-full.1.dar に対する差分バックアップファイル home-diff-1.1.dar ができる。

差分バックアップ(2回目)および増分バックアップ

もう1つファイルを追加。それから最初にあったファイルを削除してみる。

 echo 'ghi' > /tmp/dar/home/naney/file3.txt
 rm /tmp/dar/home/naney/file1.txt

ここで差分バックアップ(2回目):

 dar -c /tmp/dar/var/lib/dar/home-diff-2 \
     -A /tmp/dar/var/lib/dar/home-full \
     -y9 \
     -R /tmp/dar \
     home

home-full.1.dar に対する差分バックアップファイル home-diff-1.2.dar ができる。

またインクリメンタルバックアップもとってみる

 dar -c /tmp/dar/var/lib/dar/home-inc-2 \
     -A /tmp/dar/var/lib/dar/home-diff-1 \
     -y9 \
     -R /tmp/dar \
     home

差分バックアップファイル home-diff-1.1.dar に対する差分バックアップファイル home-diff-2.1.dar ができる。

フルバックアップからの復元
 dar -x /tmp/dar/var/lib/dar/home-full

を実行。

 home/naney/file1.txt

が復元される。

フルバックアップ+差分1回目からの復元
 dar -x /tmp/dar/var/lib/dar/home-full
 dar -x /tmp/dar/var/lib/dar/home-diff-1

を実行。

 home/naney/file1.txt
 home/naney/file2.txt

が復元される。

フルバックアップ+差分2回目からの復元
 dar -x /tmp/dar/var/lib/dar/home-full
 dar -x /tmp/dar/var/lib/dar/home-diff-2

を実行。

 home/naney/file2.txt
 home/naney/file3.txt

が復元される。

フルバックアップ+増分1回目(=差分1回目)+増分2回目からの復元
 dar -x /tmp/dar/var/lib/dar/home-full
 dar -x /tmp/dar/var/lib/dar/home-diff-1
 dar -x /tmp/dar/var/lib/dar/home-inc-2

を実行。

 home/naney/file2.txt
 home/naney/file3.txt

が復元される。

運用するには

などが必要か。 エラー処理まで含めると結構面倒くさいな。 Perlあたりでまずは簡単なスクリプトを用意するか。

[ 4月2日全て ]

2006年2月12日 (日)

野良パッケージと依存 Perl モジュールのインストールセット を CPAN::Mini::Inject

前回は CPAN::Site を用いたオフライン用インストールセットを作成した。

今回は空の CPANミラーを作り、そこに野良パッケージを突っ込んで使用する形でインストールセットを作成してみる。

~/perl-5.8.8 以下にクリーンな Perl 5.8.8 をインストールしてインストールセットを作成していく。

CPAN::Mini::Inject をインストールする (オンライン)

 perl -MCPAN -e shell
 cpan> install CPAN::Mini::Inject
 cpan> exit

CPAN::Mini::Inject は、インストールセットには必要ない。 ~/perl-5.8.8 にはインストールせずに、普段使っているほうにインストールしておく。

インストールしたい野良パッケージ用のローカル CPAN サーバを作成する (オンライン)

以下スクリプト例(エラー処理などは省略)

 #!/usr/bin/perl -w
 use strict;
 use File::Path;
 use CPAN::Mini;
 use CPAN::Mini::Inject;
 my $remote     = 'ftp://ftp.dti.ad.jp/pub/lang/CPAN/';
 my $local      = '/home/myname/public_html/CPAN';
 my $repository = '/home/myname/repository';
 mkpath([$local, $repository], 1, 0755);
 # module_filters で全てのモジュールを対象外にして、空の CPAN ミラーを作る
 CPAN::Mini->update_mirror(remote => $remote,
                           local  => $local,
                           diremode => 0755,
                           trace => 1,
                           module_filters => [ qr/./ ]);
 my $injector = CPAN::Mini::Inject->new;
 $injector->{config}{remote} = $remote;
 $injector->{config}{local} = $local;
 $injector->{config}{repository} = $repository;
 $injector->{config}{diremode} = 0755;
 # CPAN::Mini::Inject リポジトリに追加したあと、
 # CPAN ミラーへ 注入
 $injector->add(repository => $repository,
               module => 'WiKicker',
               authorid => 'NANEY',
               version => '0.xx',
               file => 'WiKicker-0.xx.tar.gz')
  ->inject;

これで、~/public_html/CPAN に野良パッケージの追加された CPAN ミラーが作成される。

WiKicker と依存するモジュールをインストールする (オンライン)

 rm -rf ~/.cpan
 ~/perl-5.8.8/bin/perl -MCPAN -e shell
 cpan> o conf urllist pop
 cpan> o conf urllist push http://http://localhost/CPAN
 cpan> reload index
 cpan> o conf urllist push ftp://ftp.dti.ad.jp/pub/lang/CPAN/
 cpan> install WiKicker
 cpan> exit

ここでローカル CPAN サーバを file:/// 等で指定すると、そこから読みとったファイルは ~/.cpan/sources/ 以下にコピーされないので一箇所にまとめることができないので注意。

またCPAN ではモジュールインデックスファイルは1組しか持てないようで、初期設定のままだと野良パッケージを含む CPAN ミラーのインデックスファイルが使われない。 そのため一旦 urllist を空にした後、 自分の CPAN ミラーを指定しインデックスファイルをロードする。 その後にソースパッケージを取得するセカンダリとして通常の CPAN (ミラー)を指定するようにしている。

これが終わると、WiKicker とそれに必要なファイルが ~/.cpan/sources にたまる。

これを適宜アーカイブして保存する。

オフラインでインストールする

別の環境で例えば /usr/local/perl-5.8.8 にインストールされた PerlWiKickerオフラインでインストールするとする。

先の工程で作成したファイルセットが /tmp/CPAN においてあるものとする。

 /usr/local/perl-5.8.8/bin/perl -MCPAN -e shell
 # 初期化でオフラインのため CPAN ミラーの選択ができずに URL の入力を
 # 求められたところで file:///tmp/CPAN を指定
 cpan> install WiKicker
 cpan> exit

これで /tmp/CPAN から芋蔓式に WiKicker がインストールされる。

ポイント

CPAN::Site を利用して構築した場合は、インストール時にも CPAN::Site が必要だが、こちらはインストールセットの利用には CPAN.pm だけで良いというのが利点。

今回は CPAN::Mini で空の CPAN ミラーを作成し野良パッケージを追加した。

ここで最初から CPAN の最新パッケージの全ミラーCPAN::Mini で作成し、これに野良パッケージを追加してインストールセットを作ってしまうという方法もある。 この場合は後で必要に応じてミラーからパッケージを入れられるというメリットがあるかわりに、ミラー作成のコストがかかるというデメリットがある。

[ 2月12日全て ]

2006年6月11日 (日)

WiKicker 0.34 リリース - 添付機能のコードを追加

2006年6月8日以来、3日ぶりのリリース。

zakwa 氏からの要望により、WikiPage のコピー直後に編集画面に移れる edit now オプションを追加。

また大きな改良として「添付機能」を追加した。 まだ最初のコードなのでエラー処理等が甘いが、それなりに動いているのでコミット。 まだ権限設定がないので、公開サーバでは使用しない方が良い。

添付ファイルのダウンロードを WiKicker 本体の CGI プログラムから行わせるか、独立の CGI プログラムにするか迷ったが、結局別物にした。

  • WiKickerURI 体系の中に、末尾にダウンロードファイル名を持ってこれる形式を作成できなかった。

というのが大きな理由。

設定方法

WiKicker のページにまだ設定方法を書いていないので、こちらへ。

attachment CGI プログラムを設置

例えば attachment というファイル名で以下のような Perl CGI プログラムを作り、Web サーバから実行できるように設定を行う。

 #!/usr/bin/perl
 use strict;
 use warniings;
 use WiKicker::WikiCGI::AttachmentController;
 WiKicker::WikiCGI::AttachmentController
   ->new(properties_file => '対応する wiki の設定ファイル名')->run;
Wiki のプロパティに設定を追加

次に Wiki の設定ファイルに以下を追加。

 param.NormalPage.attachment: enable
 param.NormalPage.attachment.uri: attachment

param.NormalPage.attachment.uri には上で作った CGI プログラムURI (相対/絶対)を指定する。

これで各ページに attachment (添付)というリンクが表示され、添付機能が使えるようになる。

WikiPage での参照の仕方
 # リンクを作成
 [[attachment:ファイル名]]
 [[attachment:ページ名/ファイル名]] <- 別のページの添付ファイル

 # 画像をインライン表示
 [[image:attachment:ファイル名]]
 [[image:attachment:ページ名/ファイル名]]
[ 6月11日全て ]

2009年12月30日 (水)

Net::OAuth を使って Twitter からフィードを取得

Twitter の replies (mentions) をフィードリーダや Plagger で読めるようにするために、Basic 認証Twitter にアクセスしてフィードをとってきてそのまま出力する Perl CGI スクリプトを書いて使っていたのだが、やはり Basic 認証が嫌なので OAuth 認証に書き換えた。

Net::Twitter モジュールだと取ってきたフィードが Perl のデータ構造になってしまいそのまま再度出力するのが面倒なので、Net::OAuth モジュールでリクエストを作って投げるようにした。

以下エラー処理等を省略したコード。

Web サイトに設置すれば(CGI スクリプト側で認証を済ませて)認証無しで replies のフィードが取れるようになるので簡単にフィードリーダで読めるようになる。

 #!/usr/bin/perl -w

 use strict;
 use warnings;
 use CGI;
 use LWP::UserAgent;
 use Net::OAuth;
 use Data::Random qw(rand_chars);

 $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;

 my $consumer_key    = 'YOUR CONSUMER KEY';
 my $consumer_secret = 'YOUR CONSUMER SECRET';
 my $access_token    = 'YOUR ACCESS TOKEN';
 my $access_token_secret = 'YOUR ACCESS TOKEN SECRET';

 my $url    = 'http://twitter.com/statuses/replies.atom';
 my $method = 'GET';

 my $query = CGI->new;

 my $request = Net::OAuth->request('protected resource')->new(
   consumer_key     => $consumer_key,
   consumer_secret  => $consumer_secret,
   request_url      => $url,
   request_method   => $method,
   signature_method => 'HMAC-SHA1',
   timestamp        => time,
   nonce        => join('', rand_chars(ssize => 16, set => 'alphanumeric')),
   token        => $access_token,
   token_secret => $access_token_secret,

   extra_params => {count => '200'});

 $request->sign;

 my $ua = LWP::UserAgent->new;
 my $response;

 if ($method eq 'GET') {
   $response = $ua->get($request->to_url);
 }
 else {
   $response = $ua->post($request->to_url);
 }

 print $query->header('application/atom+xml; charset=utf-8');
 print $response->content;
[ 12月30日全て ]

2013年1月16日 (水)

はてなブックマークのフィードをフィルタし IFTTTソーシャルメディアでシェア

リンクを複数のソーシャルメディアでシェアする仕組みを持ってなくて、そういう時は個別に投稿している。でも公開範囲とか細かく設定したい時以外はサクッと一発で終わらせた方がクールだ。あと今までほとんど Twitter ではリンクのブックマーク的シェアしていなかったんだけれど、まあ今後はちょっとしてもいいかなと。

ということで仕組み作り。はてなブックマークから Twitter 連携・外部サイト連携するのが国内的王道かな。でも他の人のリンク踏むとブックマーク先に飛んだりブックマークエントリーページに飛んだりっていう体験があってちょっとそういうの嫌だったりする。

代替案としては Facebook を一次投稿先にして IFTTT で他へ流す方法が思いついた。IFTTT 的に quick trigger になるのでタイムラグが無いのが良い。そのかわり非公開以外のリンク投稿について全て trigger してしまうようなのでちょっと怖い。URL・ページタイトル・コメントを IFTTT 側で分けて取れるのでいいんだけどね。

あとは Buffer を一次投稿先にして直接あるいは IFTTT で流す案も検討した。時間をばらしたい人はいいけどそうでない場合はあまり良くない。あとページタイトルは取得してくれない。

ということでやはりリンクの共有としては、はてなブックマークが便利なんだなぁ。PC からでも Android 端末からでもいけるし。なので一次投稿先ははてなブックマークに決定。全体公開ものをここにブックマークしているので、垂れ流れていくのも問題なし。ただ前述した点があるので、実際の連携にははてなブックマークの機能を使わないで、フィードを IFTTT に読ませて撒くことにした。

ただ IFTTTはてなブックマークにつけたコメント部分をフィードから読みとれない。ということでちょこちょこっと Perl でフィルタを書いた。

 #!/usr/bin/perl

 use warnings;
 use strict;

 use CGI;
 use LWP::UserAgent;
 use XML::RSS;

 my $ua = LWP::UserAgent->new;
 my $response = $ua->get('http://b.hatena.ne.jp/はてなID/rss');

 my $rss = XML::RSS->new;
 $rss->parse($response->content);

 foreach my $item (@{$rss->{items}}) {
   my $description = $item->{description};
   $item->{title} = $description if defined $description;
 }

 my $query = CGI->new;
 print $query->header('application/xml; charset=utf-8');
 print $rss->as_string;

サーバに XML::RSS が入ってたのでそれを使った感じ。IFTTT からは15分に1回ぐらいのアクセスのはずなので、まあこんなもんで。エラー処理も端折り。

はてなブックマークにつけたコメントは IFTTT でエントリのタイトルとしてとれるようになるので、それを使うように。Facebook に投げる場合はページのタイトルは Facebook が自前で取得してくれるので使わないので、フィードではタイトルを上書きしてしまって問題ない。

ページのタイトルを取得しないソーシャルメディア向け(Twitter とか)には

 foreach my $item (@{$rss->{items}}) {
   my $description = $item->{description};
   $item->{title} = '&#x201C;' . $item->{title} . '&#x201D;';
   $item->{title} = $description . ' / ' . $item->{title}
     if defined $description;
 }

と、タイトルとコメントをはてなブックマーク書式っぽくしてタイトルに設定してあげる。あとは IFTTT でいい感じに流してもらうと。

[ 1月16日全て ]

2013年9月20日 (金)

艦これフリーク音ゲーマーなインターンお疲れさまでした

8月16日 (金) から配属されていたインターン、今日が最終日。 YAPC::Asia Tokyo 2013 に参加していて不在だったので今日は直接挨拶できなかったけど、お疲れさまでした。

インターンの中でも一番若く (未成年)、はたしてどこまで出来るのか、期待半分・心配半分でのスタートだったんだけれど、想像以上に飲み込みがはやくて大丈夫だった。若い吸収力ってすごいね。

前半は開発環境の構築と開発プロセスの理解、開発中の既存システムへの機能追加。後半はチーム内で新しく始まった Mojolicious ベースのシステム開発の中の問い合わせフォーム開発をお願いし、無事まとめあげてくれた。

普段、主に競技プログラミングに取り組んでいるというインターンなので、今回は実ソフトウェアにおける「オブジェクト指向」「MVC 的なこと」「責務の分離」「バリデーション」「エラー処理」「テスト」あたりの要所について都度伝えたつもりだったけど、ちょっとでも何か学びになれていればよいなと思う。

青春の夏休みに我々のインターンシップを選んでくれてありがとう!

あとマンツーマンでフォローしてくれたおとよさんもお疲れさま! 引き続き艦これをお楽しみください。

[ 9月20日全て ]

2018年10月30日 (火)

JavaScript コードで TaskPaper ファイルにノートを挿入する

ちょっとしたメモを Alfred for Mac から一発で TaskPaper ファイルに挿入したい。Packal 上に TaskPaper のための多機能な Alfred Workflow があるので入れたんだけれど、うまくタスク追加ができないことがあるので、自前でスクリプトを作って Alfred から呼ぶことにした。

TaskPaper ファイルはテキストファイルなので書き慣れている Perl でスクリプトを書いてもいいんだけれど、 編集の競合が避けられるし parser も書かなくて済むしということで TaskPaperAPI を使うことにした。

JavaScript for Automation (JXA) を使えば JavaScript コードで TaskPaper API を呼べるっぽい。

以下指定した TaskPaper ファイルに Inbox: プロジェクトがなければ追加した上でその子供としてノートを挿入するコード(エラー処理割愛。実際にはタイムスタンプとかもノートにつけるようにした)。

 #!/usr/bin/env osascript -l JavaScript

 function TaskPaperContext(editor, options) {
   let inbox = editor.outline.evaluateItemPath("//Inbox:")[0];
   if (!inbox) {
     inbox = editor.outline.createItem("Inbox:");
     let projects = editor.outline.evaluateItemPath('@type = project')
     if (projects.length == 0) {
         editor.outline.root.appendChildren(inbox)
     } else {
       editor.outline.root.insertChildrenBefore(inbox, projects[0]);
     }
   }

   let items = ItemSerializer.deserializeItems(options.text, editor.outline, ItemSerializer.TEXTMimeType)
   editor.setCollapsed(items[0])
   inbox.appendChildren(items, inbox.firstChild)
 }

 function run(argv) {
   Application('TaskPaper').open(argv[0]).evaluate({
     script:TaskPaperContext.toString(),
     withOptions: {text: argv[1]}
   })
 }

これを inbox.scpt というファイルで保存し実行権限を与えれば

 ./inbox.scpt $HOME/tmp/test.taskpaper こんにちはこんにちは!!

という感じで呼び出せるようになる。

あとは Alfred Workflow を作ってそこからこのスクリプトを実行すれば OK だ。

[ 10月30日全て ]

About

Naney Naneymx

Naney (なにい) です。株式会社MIXIで SNS 事業の部長をしています。

※本サイトの内容は個人的見解であり所属組織とは関係ありません。

Process Time: 0.061756s / load averages: 0.32, 0.33, 0.33