ちょっと安めに。。意外とうまかった。
中茶屋キャンプ場です。
フリスビーとかいろいろなくしたため捜索活動。
KohanaオリジナルのORMはとってもシンプル。
シンプルすぎてそのまま使うと危険なほど。
いくつか実運用時に気になった点を修正したORMを上げてみました。
1) Column list
テーブル定義を動的にとるのがデフォルトとされています。
これは、Cakeとかでもそうだしまぁ問題ないですね。
ただ、なぜかコレがキャッシュされません。
SQL異常に多いなーと思って中見たら、
毎回show columns クエリー飛ばしているじゃありませんか。
ということでファイルに1日キャッシュ。
alterしたりしたあとはcacheディレクトリ削除すればよし。
2)Has many
デフォルトは、
$user->child->find_all();
のようにもう一回クエリを飛ばしなさいよ!ってことらしい。
Joinして絞り込みたい時もあるでしょうが。使いようだと思います。
なのでwith()を拡張して$user->with('child')でLeft joinさせます。
あまり負荷を考えるとよろしい作法ではないですが、
使いどころ次第かと思います。
3) 透過キャッシュ
せっかくrowオブジェクトを持っているのだから
レコード更新時にキャッシュ更新したり、
参照時にキャッシュからとったりは透過的にできるでしょう。
findはPKで参照している時しか対応しづらかったのでそこまでですが、
アプリ作る際に意識していればこれであらかたキャッシュは回せるかと思います。
save_with_cacheでselectしなおしているのがちょっとダサいが、
たいていの更新はもとのrow objectがloadedなので問題ないはず。
This gist provides 3 additional function for Kohana-ORM. | |
1) Cache column lists. | |
Kohana's original ORM execute "show column" query every call time. | |
I think that the column lists should be cached for long time like CakePHP. | |
2) Has many relation with Left Join query. | |
By Kohana's original ORM , has many relation query do like this. | |
-- | |
$user->childs->find_all(); | |
foreach( $model->childs as $child) | |
{ | |
print $child->name; | |
} | |
-- | |
But I want do like this. | |
-- | |
$user->with('child')->find_all(); | |
foreach($user as $row) | |
{ | |
print $user->child->name; | |
} | |
3) Cache row. | |
add save_with_cache(), find_by_cache(). | |
Cache storage is Plagable. | |
If you are using Memcached library, you can get caches by multi keys. |
<?php defined('SYSPATH') or die('No direct script access.'); | |
class ORM extends Kohana_ORM { | |
// date_created is the column used for storing the creation date. | |
// Use TRUE to store a timestamp | |
protected $_created_column = array('column' => 'created' ,'format' => 'Y-m-d H:i:s'); | |
// date_modified is the column used for storing the modified date. | |
// In this case, a string specifying a date() format is used | |
protected $_updated_column = array('column' => 'modified' ,'format' => 'Y-m-d H:i:s'); | |
protected $_reload_on_wakeup = FALSE; | |
protected $_use_cache_type = 'memcache'; | |
protected $_use_cache_expire = 600; | |
protected $_sleep_properties = array(); | |
/** | |
* Allows serialization of only the object data and state, to prevent | |
* "stale" objects being unserialized, which also requires less memory. | |
* | |
* @return array | |
*/ | |
public function __sleep() | |
{ | |
// Store only information about the object | |
$default = array('_object_name', '_object', '_changed', '_loaded', '_saved', '_sorting'); | |
return array_merge($default,$this->_sleep_properties); | |
} | |
/** | |
* Returns the values of this object as an array, including any related one-one | |
* models that have already been loaded using with() | |
* | |
* @return array | |
*/ | |
public function as_array() | |
{ | |
$object = array(); | |
foreach ($this->_object as $key => $val) | |
{ | |
// Call __get for any user processing | |
$object[$key] = $this->__get($key); | |
} | |
foreach ($this->_related as $key => $model) | |
{ | |
// Include any related objects that are already loaded | |
$object[$key] = $model->as_array(); | |
} | |
foreach($this->_sleep_properties as $var ) | |
{ | |
if(! property_exists($this,$var)) | |
{ | |
continue; | |
} | |
if( is_subclass_of($this->$var,"ORM")) | |
{ | |
$object[$var] = $this->$var->as_array(); | |
} | |
elseif (is_array($this->$var)) | |
{ | |
$object[$var] = $this->$var; | |
} | |
} | |
return $object; | |
} | |
/** | |
* Saves the current object. | |
* | |
* @chainable | |
* @return ORM | |
*/ | |
public function save() | |
{ | |
if (empty($this->_changed)) | |
return $this; | |
$data = array(); | |
foreach ($this->_changed as $column) | |
{ | |
// Compile changed data | |
$data[$column] = $this->_object[$column]; | |
} | |
if ( ! $this->empty_pk() AND ! isset($this->_changed[$this->_primary_key])) | |
{ | |
// Primary key isn't empty and hasn't been changed so do an update | |
if (is_array($this->_updated_column)) | |
{ | |
// Fill the updated column | |
$column = $this->_updated_column['column']; | |
$format = $this->_updated_column['format']; | |
$data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); | |
} | |
$query = DB::update($this->_table_name) | |
->set($data) | |
->where($this->_primary_key, '=', $this->pk()) | |
->execute($this->_db); | |
// Object has been saved | |
$this->_saved = TRUE; | |
} | |
else | |
{ | |
if (is_array($this->_created_column)) | |
{ | |
// Fill the created column | |
$column = $this->_created_column['column']; | |
$format = $this->_created_column['format']; | |
$data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); | |
} | |
if (is_array($this->_updated_column)) | |
{ | |
// Fill the updated column | |
$column = $this->_updated_column['column']; | |
$format = $this->_updated_column['format']; | |
$data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); | |
} | |
$result = DB::insert($this->_table_name) | |
->columns(array_keys($data)) | |
->values(array_values($data)) | |
->execute($this->_db); | |
if ($result) | |
{ | |
if ($this->empty_pk()) | |
{ | |
// Load the insert id as the primary key | |
// $result is array(insert_id, total_rows) | |
$this->_object[$this->_primary_key] = $result[0]; | |
} | |
// Object is now loaded and saved | |
$this->_loaded = $this->_saved = TRUE; | |
} | |
} | |
if ($this->_saved === TRUE) | |
{ | |
// All changes have been saved | |
$this->_changed = array(); | |
} | |
return $this; | |
} | |
/** | |
* begin transaction | |
*/ | |
public function begin(){ | |
if($this->_db->in_tx === FALSE){ | |
DB::query(DATABASE::UPDATE,'SET AUTOCOMMIT=0')->execute($this->_db); | |
DB::query(DATABASE::UPDATE,'BEGIN')->execute($this->_db); | |
} | |
$this->_db->in_tx = TRUE; | |
} | |
/** | |
* commit transaction | |
*/ | |
public function commit() { | |
if($this->_db->in_tx === TRUE){ | |
DB::query(DATABASE::UPDATE,'COMMIT')->execute($this->_db); | |
DB::query(DATABASE::UPDATE,'SET AUTOCOMMIT=1')->execute($this->_db); | |
} | |
$this->_db->in_tx = FALSE; | |
} | |
/** | |
* rollback transaction | |
*/ | |
public function rollback(){ | |
if($this->_db->in_tx === TRUE){ | |
DB::query(DATABASE::UPDATE,'ROLLBACK')->execute($this->_db); | |
DB::query(DATABASE::UPDATE,'SET AUTOCOMMIT=1')->execute($this->_db); | |
} | |
$this->_db->in_tx = FALSE; | |
} | |
/** | |
* Binds another one-to-one object to this model. One-to-one objects | |
* can be nested using 'object1:object2' syntax | |
* | |
* @param string target model to bind to | |
* @return void | |
*/ | |
public function with($target_path) | |
{ | |
if (isset($this->_with_applied[$target_path])) | |
{ | |
// Don't join anything already joined | |
return $this; | |
} | |
// Split object parts | |
$aliases = explode(':', $target_path); | |
$target = $this; | |
foreach ($aliases as $alias) | |
{ | |
// Go down the line of objects to find the given target | |
$parent = $target; | |
$target = $parent->_related($alias); | |
if ( ! $target) | |
{ | |
// Can't find related object | |
return $this; | |
} | |
} | |
// Target alias is at the end | |
$target_alias = $alias; | |
// Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix) | |
array_pop($aliases); | |
$parent_path = implode(':', $aliases); | |
if (empty($parent_path)) | |
{ | |
// Use this table name itself for the parent path | |
$parent_path = $this->_table_name; | |
} | |
else | |
{ | |
if( ! isset($this->_with_applied[$parent_path])) | |
{ | |
// If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail) | |
$this->with($parent_path); | |
} | |
} | |
// Add to with_applied to prevent duplicate joins | |
$this->_with_applied[$target_path] = TRUE; | |
// Use the keys of the empty object to determine the columns | |
foreach (array_keys($parent->_object) as $column) | |
{ | |
$name = $parent_path.".".$column; | |
$alias = $column; | |
// Add the prefix so that load_result can determine the relationship | |
$this->select(array($name, $alias)); | |
} | |
// Use the keys of the empty object to determine the columns | |
foreach (array_keys($target->_object) as $column) | |
{ | |
$name = $target_path.'.'.$column; | |
$alias = $target_path.':'.$column; | |
// Add the prefix so that load_result can determine the relationship | |
$this->select(array($name, $alias)); | |
} | |
if (isset($parent->_belongs_to[$target_alias])) | |
{ | |
// Parent belongs_to target, use target's primary key and parent's foreign key | |
$join_col1 = $target_path.'.'.$target->_primary_key; | |
$join_col2 = $parent_path.'.'.$parent->_belongs_to[$target_alias]['foreign_key']; | |
} | |
elseif (isset($parent->_has_many[$target_alias])) | |
{ | |
// Parent belongs_to target, use target's primary key and parent's foreign key | |
$join_col1 = $parent_path.'.'.$parent->_primary_key; | |
$join_col2 = $target_path.'.'.$parent->_has_many[$target_alias]['foreign_key']; | |
} | |
else | |
{ | |
// Parent has_one target, use parent's primary key as target's foreign key | |
$join_col1 = $parent_path.'.'.$parent->_primary_key; | |
$join_col2 = $target_path.'.'.$parent->_has_one[$target_alias]['foreign_key']; | |
} | |
// Join the related object into the result | |
$this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2); | |
return $this; | |
} | |
/** | |
* Returns an ORM model for the given one-one related alias | |
* | |
* @param string alias name | |
* @return ORM | |
*/ | |
protected function _related($alias) | |
{ | |
if (isset($this->_related[$alias])) | |
{ | |
return $this->_related[$alias]; | |
} | |
elseif (isset($this->_has_one[$alias])) | |
{ | |
return $this->_related[$alias] = ORM::factory($this->_has_one[$alias]['model']); | |
} | |
elseif (isset($this->_belongs_to[$alias])) | |
{ | |
return $this->_related[$alias] = ORM::factory($this->_belongs_to[$alias]['model']); | |
} | |
elseif (isset($this->_has_many[$alias])) | |
{ | |
return $this->_related[$alias] = ORM::factory($this->_has_many[$alias]['model']); | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
/** | |
* Proxy method to Database list_columns. | |
* | |
* @return array | |
*/ | |
public function list_columns() | |
{ | |
$cache = Cache::instance('file'); | |
$cache_id = __METHOD__."|".$this->_table_name; | |
if($ret = $cache->get($cache_id)){ | |
return $ret; | |
} | |
// Proxy to database | |
$ret = $this->_db->list_columns($this->_table_name); | |
$cache->set($cache_id,$ret,60 * 60 * 24); | |
return $ret; | |
} | |
/** | |
* キャッシュからPKでfindする | |
* @param $id primary key | |
*/ | |
public function find_by_cache($id) | |
{ | |
$cache = Cache::instance($this->_use_cache_type); | |
$cache_id = $this->get_cache_id($id); | |
if($ret = $cache->get($cache_id)){ | |
return $ret; | |
} | |
$ret = $this->find($id); | |
$cache->set($cache_id,$ret,CONFIG::$SHORT_CACHE_TIME); | |
return $ret; | |
} | |
/** | |
* PKでキャッシュをupdateする | |
* @param $id primary key | |
*/ | |
public function save_with_cache() | |
{ | |
$cache = Cache::instance($this->_use_cache_type); | |
$cache_id = $this->get_cache_id(); | |
if($ret = $this->save()){ | |
$cache->set($cache_id,$this,CONFIG::$SHORT_CACHE_TIME); | |
return $ret; | |
} | |
return $ret; | |
} | |
/** | |
* PK検索用のキャッシュIDを返却 | |
* @return string $cache_id | |
*/ | |
protected function get_cache_id($pk = null) | |
{ | |
if(is_null($pk)) | |
{ | |
return $this->_table_name."|".$this->pk(); | |
} | |
else | |
{ | |
return $this->_table_name."|".$pk; | |
} | |
} | |
public function find_by_cache_map($id_array) | |
{ | |
if(empty($id_array)) | |
{ | |
return array(); | |
} | |
if(is_string($id_array)) | |
{ | |
$id_array = array($id_array); | |
} | |
$cache = Cache::instance('memcache'); | |
$cache_id_c2i = array(); | |
$cache_id_i2c = array(); | |
$cache_id_list = array(); | |
$cached = array(); | |
foreach($id_array as $id) | |
{ | |
$cache_id = $this->get_cache_id($id); | |
$cache_id_c2i[$cache_id] = $id; | |
$cache_id_i2c[$id] = $cache_id; | |
$cache_id_list[] = $cache_id; | |
$cached[$cache_id] = null; | |
} | |
// search cache | |
$cached_data = $cache->get_multi($cache_id_list,NULL); | |
if($cached_data) | |
{ | |
$cached = array_merge($cached,$cached_data); | |
} | |
$remain_ids = array(); | |
// if cache empty, re select | |
foreach($cached as $key => $data) | |
{ | |
if(empty($data)) | |
{ | |
$remain_ids[] = $cache_id_c2i[$key]; | |
} | |
} | |
$select_data = null; | |
if(!empty($remain_ids)) | |
{ | |
$to_cache_data = array(); | |
$pk = $this->_primary_key; | |
$select_data = $this->where($pk,'IN',$remain_ids)->find_all()->as_array(); | |
foreach($select_data as $saved) | |
{ | |
$to_cache_data[$cache_id_i2c[$saved->$pk]] = $saved; | |
} | |
$cache->set_multi($to_cache_data,CONFIG::$SHORT_CACHE_TIME); | |
//print_r(array_keys($to_cache_data)); | |
$cached = array_merge($cached,$to_cache_data); | |
} | |
$result = array(); | |
foreach($cached as $key => $val) | |
{ | |
$result[$cache_id_c2i[$key]] = $val; | |
} | |
return $result; | |
} | |
} |
とうとうiMacにもFILCOつないでしまいました。
日々日々、
なんらかのWeb Application Framework (WAF)つかっているわけですが、
なんとなくCakePHPかCatalyst使うことが一番多いので、
言語すら違うのに比較した所感を。
まず、それぞれの概要から。
■Catalyst - Perl
PerlのWAF代表格ですね。
http://www.catalystframework.org/
非常に柔軟な特性を持ち、カスタマイズが簡単です。
その名の通り触媒なので、設定というか一緒に使うモジュールの組み合わせで、
ありがちな処理の殆どを吸収してくれます。
専用モジュールがCPANにガスガスあがっているのが素敵
■CakePHP - PHP
PHP版Ruby on railsですね。
ActiveRecordパターンという縛りを設けることで、
コーディング量を極端にへらしてくれます。
ドキュメントの豊富さが際立っている気がしますね。
とにかく楽したいときに最高です。
で、それぞれ仕事・プライベートで使ってますが、
なんとなく、思うところがあったり。
どちらも似たようなWAFなんですが。
一番大きく違うのは、DB関連というかModelの解釈ですかね。
CatalystはModel用ORマッパーが交換可能です。
好きなORマッパーを当て込めます。
なおかつ、ModelクラスごとにORマッパー変えられたりもします。
これがあるから、アプリレベルでのDBシェーディングなんかも表層で気を使わないですむような実装が簡単にできたり。
コアモジュールをガシガシOverrideとかしないでいいです。
CakePHPはオリジナルのORマッパーしか使えません。
でも、そのかわりにそのORマッパーのルールに従う限りめちゃくちゃ簡単です。
コード量アホかってくらい短くすみます。
なおかつなかなか直感的に使えるAPIを持っているので、
個人的にはソース綺麗になるなーなんて思っています。
Catalyst使ってもごちゃごちゃするのは自分のウデのせいですけどね。
Scaffordとか綺麗だしCakeは手抜き大好きプログラマの味方です。
Catalystはなんでも使える魔法の触媒ですが、柔軟すぎてその分コードが少し増えます。
でもCPANにある無数のモジュールを使えるので、
将来的にめんどくさい(自分で実装したくない)処理が求められそうなときは、こっちがいいかなと思います。
あと、R&D的な何するのかよく分からない自分用アプリ書くときはこれ使ってます。
毎回、アプリ作るときにどのWAF使うか悩みます。
先に言語決めろよという気もしますが。
今更だけどRailsとかも一回ちゃんと使ってみたいですね。
最近はもっぱらKohanaなんですが、
CatalystやCakeなどの歴史あるフレームワークは機能いっぱいで便利ですね。
使いどころと使い方しだいということでしょうか。
Twibbon - http://twibbon.com/
のような、Twitterアイコンになんか載せるアプリを作ろうとしてハマった。
何にはまったかというと、
update_profile_image
というTwitterAPI様が全然画像を更新しれくれないのです。
数々の先人がPHPというかPEARモジュールでハマっているようで
http://d.hatena.ne.jp/Arko/20090508/1241790310
PEARモジュールで作り始めましたが、
Services_Twitter使っても
HTTP::Request使っても
HTTP::Request2使っても
うまくいかず。
引っかかってる箇所は同じだと思うんですが、
Content-Typeと指摘しているかたも多いのでそこら辺をいじってみたりもしつつ
http://d.hatena.ne.jp/abyssluke/20090615/1245062610
これみてやっぱCurlで素直にやろうと。
PHPリコンパイルですがこれしかないですか。
で、↑のCurlのサンプルにしたがって実装できましたとさ。
TwitterAPI側のバリデーションがなんだかなという感じですが、
とりあえず実装は可能なんですねと。
Perl使ってたらはまらかったんだと思います。
ただ、PHPという与件のもとでのフィージビリティー確認でした。
Yes we can.
普段はEclipseとKomodoを利用しているんですが、
Perlやっている人たちの集まりに行くとだいたい皆さんVim || Emacsですね。
みんなつかっているということは便利なツールなんだろう
ということでVimを試しました。
Emacsじゃない理由はキーバインディング覚えればお客のサーバーでも使えるから
まずはvimrcに何を書くかを調べてみまして、
とりあえず▽書いてみたらいい感じ。
set autoindent
set tabstop=4
set number
syntax on
set term=builtin_linux
set ttytype=builtin_linux
colorscheme darkblue
set enc=utf-8
set fenc=utf-8
set fencs=iso-2022-jp,euc-jp,cp932
でもってPerlを書いてみたのですが、
Vim7からはコード補完もやってくれるそうですね
便利そうなので入れてみました。
けっこう大変だったけど
http://mig-ration.blogspot.com/2007/05/mac-vim71.html
で試してみました。
高校のころコピーバンドやってまして、
ますます買い足されたスマートフォン達。
ついでにPSP Goも買ったのでディスプレイ上マージ。
Androidについて言えば、
・HT03A
・NexusOne
・HTC Desire
・Xperia
といろいろいじりましたが、個人的にはDesireが一番好きでしたね。
Xperiaも優秀なんですが、私の小さい手には余ります。
ということで、SIMフリーDesireを購入しました。
写真上段真中がHTC直販のDesireで上段左がSoftbankのDesire。
直販の方が箱がゴージャスだった。
Xperiaはなんでか箱がでかかったですね。
Docomoの端末はおしなべて箱がでかい印象あります。
HTC Magicの時も、GoogleDeveloper'sDayで配っていた箱からくらべてずいぶんでかくなっていましたね。
話は変わってPSP Go。
不人気不人気といわれるだけあって、中古が普通のPSPと変わらないくらいの値段まで下がってきていたので衝動買い。
とりあえずバイオハザードとFF7をアーカイブスで買って遊んでます。
PSPもDSも、カセット持ち歩いたりするのが嫌なんですよね。
なくなったりするし。
ということで、PSP Go。
ただ、電池のもちがいまいっちょ。
音楽とかもマージしようかと思ったんですけど、これはiPodのままですね。
DesireにしろPSP Goにしろ、
最近充電ゲーブルがUSBで提供されるの増えてきましたね。
個人的な思いなんですけど、
USBケーブルが短すぎる!!
のですよ。
とくにPSP Goなんてパソコンの前で使うもんじゃないじゃん。
ベッドの上とかでごろごろ使いたいのに、
充電ケーブルが1メートル程度ってそれはないですよ。
いや、もっと短いか。
300円くらい高くなってもいいから考慮してもらいたいものです。
いろいろなプロジェクトやったなかで、
とりあえずこれで困らないだろう程度のコンパイルオプション。
なんか有効じゃないオプションもあるけど、
とりあえず。
突っ込み募集。
■PHP
./configure \
--with-pcre-regex \
--with-pear \
--with-apxs2=/usr/local/apache2/bin/apxs \
--with-openssl \
--with-xml \
--with-dom \
--with-xmlrpc \
--with-layout \
--with-mcrypt \
--with-gd \
--enable-zend-multibyte \
--enable-track-vars \
--enable-calendar \
--enable-mbstring \
--enable-mbstr-enc-trans \
--enable-mbregex \
--enable-bcmath \
--enable-sockets \
--enable-trans-sid \
--enable-ftp \
--enable-exif \
--with-jpeg-dir \
--with-png-dir \
--with-zlib-dir \
--with-xpm-dir \
--with-freetype-dir \
--enable-dbx \
--enable-magic-quotes \
--with-iconv \
--with-gmp \
--with-dom-xslt \
--with-dom-exslt \
--with-pgsql=/usr/local/pgsql \
--enable-soap \
--with-oci8=instantclient,/usr/lib/instantclient_10_2 \
--enable-pcntl
■Apache
./configure \
--enable-so \
--enable-rewrite \
--enable-cgi \
--enable-ssl
平日から会社休んでディズニーシーいってきました。
ほかブログからの転載なんですが、
http://github.com/taikimen/Plagger-Plugin-Filter-TinyURL
mixiにVox記事上げるために作ったんですが、
せっかくなのでりファクトしてさらしてみました。
はじめてgit使ったので今後どうすることやら。
はじめてシリーズなので大目に見てください。
内容はPerlモジュールです。
CPAN形式になっているので、扱いはわかりやすいかと。
やってることもシンプルで、body内のテキストでURLにマッチしたものをすべてtinyURLに変換するだけです。
こんなんどうでしょう?
svn export http://redmine.rubyforge.org/svn/branches/0.8-stable redmine
gem install -v=2.1.2 rails
rake config/initializers/session_store.rb
rake db:migrate RAILS_ENV=demo
rake load_default_data RAILS_ENV=demo
SQLiteで使う限り甚だ簡単ですね。
スタンドアロンサーバーでの動作もひどくはないですし。
http://redmine.jp/tech_note/install/
ここを参考にしました。
OSSっていいですね。
set long 2000
set heading off
set line 120
set pages 2000
set trimspool off
set feedback off
set longchunksize 600
select dbms_metadata.get_ddl('TABLE', 'テーブル名') from dual;
3系のドキュメントにunittestがないので記録。
pear install phpunit/PHPUnit
<?php
/**
* @group somegroup.morespecific.annoyingstuff
* @author shimazu
*
*/
Class ExampleTest extends Kohana_Unittest_TestCase
{
function setUp(){
parent::setUp();
}
function tearDown(){
parent::tearDown();
}
function providerStrLen()
{
return array(
array('One set of testcase data', 24),
array('This is a different one', 23),
);
}
/**
* @dataProvider providerStrLen
*/
function testStrLen($string, $length)
{
$this->assertSame(
$length,
strlen($string)
);
}
/**
* @dataProvider providerStrLen
*/
function testError($string, $length){
$this->assertSame(
$length,
strlen($string) + 1
);
}
}
javadoc形式でのアノテーションにてグループ指定などが可能。
データプロバイダなんておしゃれなものも同様に指定可能
実行結果は以下
リリース当日にも関わらず、
SELECT
A.USERNAME,
count(*)
FROM v$session a,
v$sesstat b,
v$sess_io c,
v$statname d
WHERE a.sid = b.sid
AND b.sid = c.sid
AND b.statistic# = d.statistic#
AND d.name like '%CPU%session'
group by A.USERNAME
ORDER BY a.username
;
SET LINESIZE 500;
SET PAGESIZE 80;
SET COLSEP |;
SET FEEDBACK OFF;
COLUMN "SID/SERIAL" FORMAT A15;
COLUMN USERNAME FORMAT A10;
COLUMN "SID/SERIAL" FORMAT A15;
COLUMN OSUSER FORMAT A10;
COLUMN MACHINE FORMAT A15;
COLUMN TERMINAL FORMAT A10;
COLUMN PROGRAM FORMAT A35;
COLUMN TYPE FORMAT A10;
COLUMN COMMAND FORMAT A20;
COLUMN COMMANDid ;
COLUMN "経過時間(sec) " FORMAT 9,999,999,990;
COLUMN "CPU使用時間(sec)" FORMAT 9,999,990.000;
COLUMN "CPU占有率(%)" FORMAT 990.000;
COLUMN "ブロック取得回数" FORMAT 9,999,999,990;
COLUMN "物理読み込み回数" FORMAT 9,999,999,990;
TTITLE LEFT '【 セッションの確認 】' SKIP 1 LINE ;
SELECT A.SID || ',' || A.SERIAL# "SID/SERIAL",
A.OSUSER,
A.MACHINE,
A.TERMINAL,
A.PROGRAM,
A.TYPE,
A.USERNAME,
A.STATUS,
DECODE(A.COMMAND ,1,'CREATE TABLE' ,2,'INSERT' ,3,'SELECT' ,
4,'CREATE CLUSTER' ,5,'ALTER CLUSTER' ,6,'UPDATE' ,7,'DELETE' ,
8,'DROP CLUSTER' ,9,'CREATE INDEX' ,10,'DROP INDEX' ,
11,'ALTER INDEX' ,12,'DROP TABLE' ,13,'CREATE SEQUENCE' ,
14,'ALTER SEQUENCE' ,15,'ALTER TABLE' ,16,'DROP SEQUENCE' ,
17,'GRANT' ,18,'REVOKE' ,19,'CREATE SYNONYM' ,20,'DROP SYNONYM' ,
21,'CREATE VIEW' ,22,'DROP VIEW' ,23,'VALIDATE INDEX' ,
24,'CREATE PROCEDURE' ,25,'ALTER PROCEDURE' ,26,'LOCK TABLE' ,
27,'NO OPERATION' ,28,'RENAME' ,29,'COMMENT' ,30,'AUDIT' ,
31,'NOAUDIT' ,32,'CREATE DATABASE LINK' ,33,'DROP DATABASE LINK' ,
34,'CREATE DATABASE' ,35,'ALTER DATABASE' ,36,'CREATE ROLLBACK SEG' ,
37,'ALTER ROLLBACK SEGM' ,38,'DROP ROLLBACK SEGME' ,
39,'CREATE TABLESPACE' ,40,'ALTER TABLESPACE' ,41,'DROP TABLESPACE' ,
42,'ALTER SESSION' ,43,'ALTER USE' ,44,'COMMIT' ,45,'ROLLBACK' ,
46,'SAVEPOINT' ,47,'PL/SQL EXECUTE' ,48,'SET TRANSACTION' ,
49,'ALTER SYSTEM SWITCH' ,50,'EXPLAIN' ,51,'CREATE USER' ,
52,'CREATE ROLE' ,53,
'DROP USER' ,54,'DROP ROLE' ,55,'SET ROLE' ,56,'CREATE SCHEMA' ,
57,'CREATE CONTROL FILE' ,58,'ALTER TRACING' ,59,'CREATE TRIGGER' ,
60,'ALTER TRIGGER' ,61,'DROP TRIGGER' ,62,'ANALYZE TABLE' ,
63,'ANALYZE INDEX' ,64,'ANALYZE CLUSTER' ,65,'CREATE PROFILE' ,
66,'DROP PROFILE' ,67,'ALTER PROFILE' ,68,'DROP PROCEDURE' ,
69,'DROP PROCEDURE' ,70,'ALTER RESOURCE COST' ,
71,'CREATE SNAPSHOT LOG' ,72,'ALTER SNAPSHOT LOG' ,
73,'DROP SNAPSHOT LOG' ,74,'CREATE SNAPSHOT' ,
75,'ALTER SNAPSHOT' ,76,'DROP SNAPSHOT' ,
79,'ALTER SESSION' ,85,'TRUNCATE TABLE' ,86,'TRUNCATE COUSTER' ,
88,'ALTER VIEW' ,91,'CREATE FUNCTION' ,92,'ALTER FUNCTION' ,
93,'DROP FUNCTION' ,94,'CREATE PACKAGE' ,95,'ALTER PACKAGE' ,
96,'DROP PACKAGE' ,97,'CREATE PACKAGE BODY' ,
98,'ALTER PACKAGE BODY' ,99,'DROP PACKAGE BODY' ,'待機中') COMMAND ,
A.COMMAND commandid,
(sysdate - a.logon_time)*86400 "経過時間(sec)",
b.value /100 "CPU使用時間(sec)",
b.value /((sysdate - a.logon_time)*86400+1) "CPU占有率(%)",
c.block_gets "ブロック取得回数",
c.physical_reads "物理読み込み回数"
FROM v$session a,
v$sesstat b,
v$sess_io c,
v$statname d
WHERE a.sid = b.sid
AND b.sid = c.sid
AND b.statistic# = d.statistic#
AND d.name like '%CPU%session'
ORDER BY a.username,
a.sid;
地味ーに自分用Twitter Browserとして利用しているTwitTagsですが、
PHPでOpenSocialなアプリを作ろうと思いまして。
あとで読んどくリスト。
http://d.hatena.ne.jp/foosin/20090326/1238077646
http://blog.livedoor.jp/sasata299/archives/51210958.html
http://d.hatena.ne.jp/foosin/20090317/1237308976
http://d.hatena.ne.jp/foosin/20090323/1237815364
http://blog.hide-k.net/archives/2007/10/catalystmodelad.php
よくわすれるのでメモ。
コピペ用。
■固定長でのCSV出力
set echo off
set linesize 1000
set pagesize 0
set trimspool on
set feedback off
set colsep ','
spool c:\temp\emp.csv
select * from emp;
E01,D01,名字1 ,名前1 , 10
E02,D01,名字2 ,名前2 , 50
E03,D02,名字3 ,名前3 , 100
spool off
■可変長でのCSV出力
set echo off
set linesize 1000
set pagesize 0
spool c:\temp\emp.csv
select emp_id ||','|| dept_id ||','|| first_name ||','|| last_name ||','||salary from emp;
E01,D01,名字1,名前1,10
E02,D01,名字2,名前2,50
E03,D02,名字3,名前3,100
spool off
めんどくさいですね
SAStrutsでアプリを書く機会がございましたので雑感を。
・Seasar2
Dolteng様の効果によりあまり意識することなくアプリがかけましたね。
SAStrursの雛形にマージされているというか。。
HOT Deployはきもちよかったですね。
PHPとかとかわならい感覚で開発できました。
・Dolteng
Seasar2のフレームワーク用Eclipseプラグイン
これのおかげでなーんも気にすることなくアプリ雛形が生成されてきました。
TEST用ソースツリーまで自動生成いただいてしまい、
らくちんきわまりない。
・SAStruts
Strutsの現代版ラッパーということのようですが、
快適すぎてはまりそうです。
綺麗にフルスタックなので、とくに悩むことなくガリガリ作っていけるかと。
Seasar2が各コンポーネントの呼び込みをやってくれるので、
オレオレライブラリとの結合も簡単。
ビジネスロジックをPojoで書きすすめられるので、
フレームワーク依存した脳みそにならないで済みそうです。
・S2JDBC
流れるようなIFのORマッパーということですね。
流れるように検索出来ます。更新できます。
ぐらいしかいいようがないですね。簡単快適。
・S2Dxo
今回、
DBのEntity <-> Dto <-> Json
というフローのAPIアプリだったので、
Entity <-> Dto変換で利用。
これまた何を定義しないでも綺麗に変換してくれるもので。
感謝感激ですね。
・JSONIC
Seasar2とは関係ないですが、素晴らしいライブラリでした。
Dto <-> Json
の変換で利用していたのですが、
変換時に変換先のクラスを指定するだけで、多重のオブジェクトまで、しっかり変換。
プロパティがListであればそれもしっかり詰めてくれます。
2環境のJavaでJsonを橋渡しにやりとりをするアプリだったんですが、
気持ちイイくらいさくさくデータの出し入れができました。
しかもJsonだから中身デバッグしやすい。
感謝。
とまぁ、なかなか最新の構成でJava開発をしてみました。
Javaのめんどくさいイメージはほぼほぼ払拭することができるのではないかなーとか、
Javaってまだまだいけてんじゃね?
という感想を持てましたね。
プロダクション環境へのデプロイはPHPとかよりは手間かかりますが、
これは更新頻度によっては問題にならないですかね。
次はSlim3によるAppEngineが楽しみですね。
遊びたい。
MacPortでインストールしたPHP5.3では、
> cd /tmp
> mkdir php5mysql5
> ln -s /opt/local/lib/mysql5 php5mysql5/lib
> ln -s /opt/local/include/mysql5 php5mysql5/include
>
> Then use --with-mysql=/tmp/php5mysql5 in your php configure line
> instead. Then delete /tmp/php5mysql5 when you're done.
利用したのでインストールメモ
Voxの皆様や、Hatenaから自動投稿死たりしている方のモノをぱくってますが
まあ需要あるんだろう ということで記載
global:
assets_path: /usr/local/share/assets/
timezone: Asia/Tokyo
log:
level: debug
plugins:
- module: Subscription::Config
config:
feed:
- url: http://taikimen.vox.com/library/posts/atom-full.xml
- module: Filter::Reverse
- module: Filter::Rule
rule:
module: Deduped
path: /home/shimazu/.vox2mixi.db
#エントリ内の画像を取得して最初の3枚を投稿
- module: Filter::FindEnclosures
- module: Filter::FetchEnclosure
config:
dir: /tmp
#エントリ内の<a> <img>を変換
- module: Filter::Regexp
config:
regexp: s//([m:133]$1)/g
- module: Filter::Regexp
config:
regexp: s/(.*?)<\/a>/link:$1/g
- module: Filter::Regexp
config:
regexp: s/.*enclosure-asset-name.*\n//g
- module: Filter::TinyURL
- module: Publish::MixiDiary
config:
"username": "hoge@hoge.com"
"password": "hogehoge"
画像の投稿は失敗してますね。
デバッグする気も起きません。
元々はTitle
のように、<a hreg="url">Title</a>の書式が対象のようだが、
個人的には<a hreg="url">url</a>とするので、TinyURLプラグイン追加
というかPluginつくった。
http://github.com/taikimen/Plagger-Plugin-Filter-TinyURL
Vox上での改行は詰まってMixiに表示されるので、
結局投稿した後にすこし調整したりしてます。
でも、本文マルチポストとかより楽なので重宝してます。
#!/usr/bin/perl -w
use strict;
use Net::Twitter;
my $user_id = 'taikimen';
my $nt = Net::Twitter->new(
username => 'taikimen',
password => '********'
);
binmode(STDOUT, ":utf8");
for (my $cursor = -1, my $fol; $cursor; $cursor = $fol->{next_cursor} ){
$fol = $nt->followers({'screen_name' => $user_id, cursor => $cursor});
foreach my $user(@{$fol->{users}}){
print $user->{name}.",".$user->{screen_name}."\n";
}
}
exit 0;
懸賞とかで使えるんじゃないかなー。
Authenticateで指定したユーザーじゃないと、
TL非公開ユーザーがとれませぬ。