ROUTE 3390

備忘録的な用途で書いていますが、どなたかの役に立つ事があれば嬉しいです。

Mouse(Moose)のRoleにはBUILDメソッドを書くべきではないんだね

下記のようなソースがあった。

Sasakure.pm

package Sasakure;
use Mouse;

with 'SasaRole';

__PACKAGE__->meta->make_immutable;
no Mouse;

1;

SasaRole.pm

package SasaRole;

use Mouse::Role;
no Mouse::Role;

sub BUILD {
    my $self = shift;
    warn "sasarole";   #「sasarole」 がエラーログに吐かれる
}

1;


Moose初心者の僕は、Sasakure.pmでもBUILDメソッド使いたいな〜 なんて軽い気持ちで
Sasakure.pmにBUILDメソッドを追加したら、SasaRole.pmのBUILDメソッドが実行されなくなった。

Sasakure.pm

package Sasakure;
use Mouse;

with 'SasaRole';

__PACKAGE__->meta->make_immutable;
no Mouse;

sub BUILD {    # 実行される
    my $self = shift;
    warn "sasakure";  #「sasakure」 がエラーログに吐かれる
}

1;

SasaRole.pm

package SasaRole;

use Mouse::Role;
no Mouse::Role;

sub BUILD {    # 実行されない
    my $self = shift;
    warn "sasarole";  #「sasarole」 はエラーログに出ない
}

1;


よくよく考えると、withすることでSasaRole.pmのプロパティやらメソッドはSasakure.pmに継承されている。
もちろんSasaRole.pmのBUILDメソッドも、、、


んでSasakure.pmではwithした後に、BUILDメソッドを定義している。。。
もしかして、同じメソッド名はオーバーライドされちゃうんじゃない?

上と同じだけどSasakure.pm

package Sasakure;
use Mouse;

with 'SasaRole';  # SasaRoleを我が一部とする もちろんBUILDメソッドも

__PACKAGE__->meta->make_immutable;
no Mouse;

sub BUILD {    # SasaRoleにもBUILDメソッドあったけど、こっちを使おうぜ
    my $self = shift;
    warn "sasakure";  #「sasakure」 がエラーログに吐かれる
}

1;


ということは、そもそもRoleモジュールでBUILDメソッドは使うべきではないよね。

BUILDARGS や DEMOLISH も同じだろうから気を付けよう。

CGI::Application::DispatchとMouse(Moose)を使った時にハマったこと

CGI::Application::Dispatchで下記のようにargs_to_newを使うと、
実行するモジュールへの引数が指定出来る。

index.cgi

#!/usr/bin/perl

use strict;
use warnings;

use CGI::Application::Dispatch;

CGI::Application::Dispatch->dispatch(
    prefix  => 'Sasakure',
    table   => [
        ''    => { app => 'Blog', rm => 'posts' },
    ],
    args_to_new => {
        PARAMS => {
            foo => 'bar'
        },
    }
);

exit;

Sasakure/Blog.pm

package Sasakure::Blog;

use strict;
use warnings;

use base 'CGI::Application';

sub posts {
    $self = shift;
    warn $self->PARAMS->{foo};   # エラーログに「bar」が吐かれる。
}

1;

よーし!ディスパッチはできたから、最近かじっているMouseを利用してみよう!
という時に思わぬ事件。。

下記のように書いたらindex.cgiからパラメータが受け取れなくなってしまった。
Sasakure/Blog.pm

package Sasakure::Blog;

use base 'CGI::Application';

use Mouse;

__PACKAGE__->meta->make_immutable;
no Mouse;

sub posts {
    $self = shift;
    warn $self->PARAMS->{foo};   # そんなんねーよとエラー
}

1;


原因はnewにあった。
MooseやMouseで__PACKAGE__->meta->make_immutable;をすると、newというコンストラクタは呼ばれなくなる。
代わりにBUILDという名前のメソッドがその役割を担ようになる。


CGI::Application::Dispatchでは,コールするモジュールのnewメソッドとmode_paramメソッドを実行しようとする。

んで、そのときにargs_to_newで渡されたパラメータを受け取る。
それを1行で済ませるのが、use base qw/CGI::Application/;だ。
継承することで、newメソッドとか書く必要がなくなる。


でも何度も言うようにMouseでmake_immutableすると、、newが呼ばれなくなる。
というわけで下記のように書くことで、無事index.cgiからパラメータを受け取れるようになった。

Sasakure/Blog.pm

package Sasakure::Blog;

#use base 'CGI::Application';

use Mouse;

# index.cgiからのパラメータをしまう場所を用意
has 'params' => (
    is       => 'rw',
    isa      => 'ArrayRef[Str]',
);

__PACKAGE__->meta->make_immutable;
no Mouse;

sub BUILD { # newに代わるメソッドでパラメータを受け取る
    my $self = shift;
    my ( $dispatch_args, ) = @_;
    $self->{params} = $dispatch_args->{PARAMS};
}

sub mode_param { # これ使わなくても用意しとかないとDispatchがエラー出しよる
    my $self = shift;
    return $self;
}

sub posts {
    $self = shift;
    warn $self->params->foo;   # 無事、エラーログに「bar」が吐かれる。
}

1;

MySQLでテーブルサイズを調べる方法のメモ

▼全てのテーブルサイズをGB(ギガバイト)で取得
SELECT SUM(data_length)/1024/1024/1024 as db_size_GB FROM information_schema.tables WHERE table_schema='データベース名';


▼特定のテーブルサイズをMB(メガバイト)で取得
SELECT SUM(data_length)/1024/1024 as db_size_MB FROM information_schema.tables WHERE table_schema='データベース名' AND table_name='テーブル名';


▼インデックスのサイズを調べる
data_lengthをindex_lengthにすればok

ScalaでJavaのListをforにぶっこんだら怒られた

エラーはこんな感じ。
value foreach is not a member of java.util.List[String]


for(foreach)はJavaのListを扱えないんですって。

じゃあどうしたらforで扱えるのさ?


型変換を行えば良いらしい。

まずは必要なモジュールをインポート。

import scala.collection.JavaConversions._

そして

for (scala_string <- asScalaBuffer(java_list)) {
  << 省略 >>
}

できたーーー!


他のメソッドはこちらを参考にどうぞ
http://www.scala-lang.org/api/rc/scala/collection/JavaConversions$.html



ちなみに、asList()でも出来るけど、廃止予定らしいのでwarning出ると思います。

Entity-Attribute-Valueモデル

ECパッケージ Magento で使われているEAVモデルについて、自分なりに理解できているか確認の意味も込めてまとめます。
誤りなど気づいた方がいらしたら、是非つっこんでください。

まずよくあるユーザーテーブル
▼customer_table

id name birthday address
1 sasakure 1984/12/03 tokyo shibuya
2 tarou 2002/01/22 kanagawa kawasaki

これをEAVモデルにすると下記のようになります。

まずはEntity
▼entities_table

id
1
2

Entityは「実態」とか「実在」という意味です。
この場合「ユーザ」を表します。
最低でもidだけあればEntityとして充分です。

次にAttribute
▼attributes_table

id attributes_name
1 name
2 birthday
3 address

Attributeは「属性」という意味です。
entityが持つ属性を管理します。

そしてValue
▼values_table

id entities_id attributes_id value
1 1 1 sasakure
2 1 2 1984/12/03
3 1 3 tokyo shibuya
4 2 1 tarou
5 2 2 2002/01/22
6 2 3 kanagawa kawasaki

Valueは「値」なので、EntityとAttributesの組み合わせに対して値を管理します。


テーブル数は増え、value_tableのレコード数も増えますね。
これがソートされてなかったりしたら、テーブルをパッと見ただけでは
どれがどのユーザーのデータなのかは分かりません。
とあるユーザーの情報を集めるのもSQLならJOINしたりと面倒。

ん、ではなぜEAVモデルを使うのか。
それは柔軟に属性を変えられるからです。

例えばユーザーテーブルに「email」を追加したい場合、従来ならSQLでALTER TABLE文を使います。
もし、既に沢山のユーザーがいる場合は、更新も一苦労です。
例えばですが、とあるユーザーだけ属性を増やしたい場合があったとしても、全ユーザーに属性を増やす事になりますね。

でもEAVモデルならattributes_tableに属性「email」を増やすだけで済みます。
必要なentity(ユーザー)にだけvalueを設定してあげればいいのです。


では、ユーザーテーブルだけでなく、商品テーブルもEAVモデルにしましょう。
それぞれでentities_tableなんかを作る事もできますが、下記のようにすれば
同じテーブルで管理出来るようになります。

▼entity_types_table

id name
1 customer
2 product

entityの種類を管理するテーブルを新設

▼entities_table

id entity_types_id
1 1
2 1
3 2
4 2

customerとproductどちらのentityか分かるようにします。

▼attributes_table

id entity_types_id attributes_name
1 1 name
2 1 birthday
3 1 address
4 2 name
5 2 price
6 2 image

attributeにも商品情報用の属性を追加すればOKです。
values_tableはそのままで構いません。


どうでしょう? こんなデータの管理方法もあるんですねー。

SQLを書くには面倒な構造ですが、O/RマッパやNoSQLの利用が進むにつれ
EAVモデルを採用するケースも多くなるんだと思いました。

HttpURLConnectionでハマったこと

Androidアプリの開発に携わるようになったので、ちょっとそちらでハマった話。

アプリからどこぞのAPIを叩く時など、httpやhttpsで通信を行います。
その時利用したのが

java.net.HttpURLConnection

これを使うのがスタンダードみたい。
それでメソッドにはGETやPOST、PUTやDELETEなどを
setRequestMethod(String)で指定するわけだけど、

POSTを使う場合には setDoOutput(true) をしないといけない。
ただしGETの時にtrueを指定しても動作するから、
どんなメソッドを使う時もとりあえずsetDoOutput(true)にしてた。


で、実際使っていたら、GETメソッドでコールするAPI
なぜかPOSTでコールしちゃう端末が現れてしまった。
4.0系の端末だけがPOSTになる。。


明示的にGETを指定しているにも関わらず、POSTになっちゃう。。。
途方に暮れていたら、こんな記事が
http://webdiary.com/tag/setdooutput/
Android4.0から仕様が変わり、
setDoOutput(true)にすると、いくらsetRequestMethod(String)で指定しても
POSTになるらしい。


よく見るとandroid developersのサイトにも書いてあった。
http://developer.android.com/reference/java/net/HttpURLConnection.html



各バージョンでのテストは重要だな〜

ブロック暗号方式に挑戦。 Mcryptを使ってみた

McryptでCBCモードを使う機会があったのでメモ

use strict;
use warnings;

use Mcrypt qw(:ALGORITHMS :MODES);
use Digest::MD5 qw(md5_hex);

my $FILE_NAME = 'kkkkk.txt';
my $FILE = '/home/sasakure/' . $FILE_NAME;

my $td = Mcrypt->new(
    algorithm => Mcrypt::RIJNDAEL_128,
    mode => Mcrypt::CBC,
  );

warn "key length "   . $td->{KEY_SIZE}   . "\n";  # 16
warn "iv length "    . $td->{IV_SIZE}    . "\n";  # 16
warn "block length " . $td->{BLOCK_SIZE} . "\n";  # 16
# cipherによってそれぞれのサイズは変わってくる(確認してないけどたぶん)

my $key = md5_hex('mynameis'); # md5はとりあえず16バイトになるから使っているだけ
my $iv  = md5_hex('sasakure');

$td->init( pack('H*', $key), $iv);

open(my $fh, '<', $FILE) || die $!;
while ( read ( $fh, my $buf, 16 ) ) {
  # ブロックサイズに合わせて暗号化しないと警告が出る。
  my $residue = length($buf) % 16;
  my $padding_num = 16 - $residue;
  if ($residue > 0) {
    # 最後16バイトに満たない場合にパディングする。
    map { $buf .= $_ } pack('C*', map { 0x00 } (1..$padding_num));
  }
  print $td->encrypt( $buf );
}
close($fh);

exit;


そもそもブロック暗号方式というものに初めて触れたので、それもメモ


ブロック暗号について
暗号化したいデータを一定の長さに区切って暗号化します。


いくつかモードがあるけれど、僕が理解したのは下記2つ
ECBモード
1つの鍵で暗号化を行います。なので、鍵さえ知っていれば全てのブロックを複合化出来る事になります。
ランダムアクセスが出来る事も特徴の1つです。


CBCモード
Cipher Block Chainingの略です。チェーンという単語から何かが繋がっている感じがイメージ出来るかと思います。
鍵はECBと同様に1つですが、それだけではなく、隣のブロックの暗号化した結果を鍵と一緒に暗号化の材料とします。


暗号化したブロックと鍵を合わせて、新しい鍵を作ります。
その新しい鍵を使って次のブロックを暗号化します。
そこで暗号化されたブロックと、鍵をまた合わせて、更に次のブロックを暗号化します。


逆に複合化する時は隣のブロックが必要になります。
という事はECBのようにランダムアクセスは出来ません。
その代わりにECBよりも推測されづらくなるわけです。


ちなみに一番最初のブロックはiv(イニシャライズ ベクター)という値を使います。
一番最初のブロックは「暗号化された隣のブロック」が無いのでその代わりですね。