スタッフブログ
satoです。
テストと言えばユニットテストとかはだいぶ充実してきましたが、機械的なコードになりがちなので、プログラマ以外のテストケースの検証が難しいという問題があり、別途説明用にテストケースを書き直したりする必要がありました。
Ruby on Railsの話になりますが、Cucumberというツールがあるようです。
これは、YAMLで受け入れテストを書くことによって、Railsアプリのテストを行えるようにするものです。
ちなみにこれは正確にはBDD(Behavior Driven Development/ビヘイビア駆動開発)のためのツールらしいです。
Cucumberのページを開くと、以下のようなものが記述されています。
見た感じだとただの英文に見えます(YAMLっぽい部分が若干ありますが)が、Cucumberではこれをテストコードとして解釈して実行させることができます。
■インストール
CucumberはRails2.1以降で動作します。
でインストールできます。
また、別途
あたりをインストールしておくといいようです。
■テスト用プロジェクト作成
とりあえずブログアプリケーションのようなものを作る想定で作ります。
http://127.0.0.1:3000/entries/とかで、表示ができていることを確認してください。
■Cucumber初期化
で、いろいろ生成されます。
で、Entryに対するテストコードの雛形が生成されます。
features/manage_entries.feature
features/step_definitions/entry_steps.rb
このうち、manage_entries.featureが、実際のテストコードになります。このファイルを開くと以下のように書かれています。
Featureは、このテストシナリオ全体の内容を記載します。(Featureの直後に書いてあるのは概要のようなものです)
実際の動作は、各Scenarioに記述していきます。
とりあえずScenarioを書いてみます。
これで、
すると、以下のようにテストが実行されます。
passedとなっているので、正常終了しているようです。
試しに失敗させてみます。
app/controllers/entries_controller.rb
すると以下のようになると思います。
削除をコメントアウトしたので、削除の部分が失敗しているのがわかると思います。
このような感じでテストを書くことができるようになります。
英語だと若干わかりづらいのですが、有志の手によって一部を日本語で書けるようにしたものもあるようです(今回は省略します)。また、同時に生成されたstepファイル(今回だとentry_steps.rb)で、細かい実行内容の定義を書くことができます。これらを組み合わせることにより、自然な日本語でテストを書くことができるようにもなります。
従来のテストコードに比べて視認性が格段に良くなりますので、Railsで開発をしている方は是非試してみてください。
テストと言えばユニットテストとかはだいぶ充実してきましたが、機械的なコードになりがちなので、プログラマ以外のテストケースの検証が難しいという問題があり、別途説明用にテストケースを書き直したりする必要がありました。
Ruby on Railsの話になりますが、Cucumberというツールがあるようです。
これは、YAMLで受け入れテストを書くことによって、Railsアプリのテストを行えるようにするものです。
ちなみにこれは正確にはBDD(Behavior Driven Development/ビヘイビア駆動開発)のためのツールらしいです。
Cucumberのページを開くと、以下のようなものが記述されています。
Feature: Search courses
In order to ensure better utilization of courses
Potential students should be able to search for courses
Scenario: Search by topic
Given there are 240 courses which do not have the topic "biology"
And there are 3 courses A,B,C that each have "biology" as one of the topics
When I search for "biology"
Then I should see the following courses:
| title |
| A |
| B |
| C |
■インストール
CucumberはRails2.1以降で動作します。
gem install rspec rspec-rails cucumber webrat
また、別途
gem install term-ansicolor treetop diff-lcs nokogiri builder
■テスト用プロジェクト作成
とりあえずブログアプリケーションのようなものを作る想定で作ります。
rails blog
cd blog
script/generate scaffold entry name:string title:string body:text
rake db:migrate
■Cucumber初期化
script/generate cucumber
script/generate feature Entry
features/manage_entries.feature
features/step_definitions/entry_steps.rb
このうち、manage_entries.featureが、実際のテストコードになります。このファイルを開くと以下のように書かれています。
Feature: Manage entries
In order to [goal]
[stakeholder]
wants [behaviour]
Scenario: Register new entry
Given I am on the new entry page
And I press "Create"
Scenario: Delete entry
Given the following entries:
||
||
||
||
||
When I delete the 3rd entry
Then I should see the following entries:
||
||
||
||
実際の動作は、各Scenarioに記述していきます。
とりあえずScenarioを書いてみます。
Feature: Manage entries
In order to [goal]
[stakeholder]
wants [behaviour]
Scenario: Register new entry
Given I am on the new entry page
When I fill in "Name" with "user"
And I fill in "Title" with "blog title"
And I fill in "Body" with "blog content"
And I press "Create"
Then I should see "user"
And I should see "blog title"
And I should see "blog content"
Scenario: Delete entry
Given the following entries:
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name3 | title3 | body3 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
When I delete the 3rd entry
Then I should see the following entries:
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
rake features
すると、以下のようにテストが実行されます。
/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/lib:lib" "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/bin/cucumber" --format pretty features/manage_entries.feature
Feature: Manage entries
In order to [goal]
[stakeholder]
wants [behaviour]
Scenario: Register new entry # features/manage_entries.feature:6
Given I am on the new entry page # features/step_definitions/webrat_steps.rb:6
When I fill in "Name" with "user" # features/step_definitions/webrat_steps.rb:22
And I fill in "Title" with "blog title" # features/step_definitions/webrat_steps.rb:22
And I fill in "Body" with "blog content" # features/step_definitions/webrat_steps.rb:22
And I press "Create" # features/step_definitions/webrat_steps.rb:14
Then I should see "user" # features/step_definitions/webrat_steps.rb:93
And I should see "blog title" # features/step_definitions/webrat_steps.rb:93
And I should see "blog content" # features/step_definitions/webrat_steps.rb:93
Scenario: Delete entry # features/manage_entries.feature:16
Given the following entries: # features/step_definitions/entry_steps.rb:1
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name3 | title3 | body3 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
When I delete the 3rd entry # features/step_definitions/entry_steps.rb:5
Then I should see the following entries: # features/step_definitions/entry_steps.rb:12
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
2 scenarios (2 passed)
11 steps (11 passed)
0m1.040s
passedとなっているので、正常終了しているようです。
試しに失敗させてみます。
app/controllers/entries_controller.rb
def destroy
@entry = Entry.find(params[:id])
# @entry.destroy # ここをコメントアウト
respond_to do |format|
format.html { redirect_to(entries_url) }
format.xml { head :ok }
end
end
rake features
すると以下のようになると思います。
/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/lib:lib" "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/b
in/cucumber" --format pretty features/manage_entries.feature
Feature: Manage entries
In order to [goal]
[stakeholder]
wants [behaviour]
Scenario: Register new entry # features/manage_entries.feature:6
Given I am on the new entry page # features/step_definitions/webrat_steps.rb:6
When I fill in "Name" with "user" # features/step_definitions/webrat_steps.rb:22
And I fill in "Title" with "blog title" # features/step_definitions/webrat_steps.rb:22
And I fill in "Body" with "blog content" # features/step_definitions/webrat_steps.rb:22
And I press "Create" # features/step_definitions/webrat_steps.rb:14
Then I should see "user" # features/step_definitions/webrat_steps.rb:93
And I should see "blog title" # features/step_definitions/webrat_steps.rb:93
And I should see "blog content" # features/step_definitions/webrat_steps.rb:93
Scenario: Delete entry # features/manage_entries.feature:16
Given the following entries: # features/step_definitions/entry_steps.rb:1
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name3 | title3 | body3 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
When I delete the 3rd entry # features/step_definitions/entry_steps.rb:5
Then I should see the following entries: # features/step_definitions/entry_steps.rb:12
| name | title | body |
| name1 | title1 | body1 |
| name2 | title2 | body2 |
| name4 | title4 | body4 |
| name5 | title5 | body5 |
expected: "name4",
got: "name3" (using ==)
Diff:
@@ -1,2 +1,2 @@
-name4
+name3
(Spec::Expectations::ExpectationNotMetError)
./features/step_definitions/entry_steps.rb:16
./features/step_definitions/entry_steps.rb:15
(eval):3:in `each_with_index'
./features/step_definitions/entry_steps.rb:14:in `each'
./features/step_definitions/entry_steps.rb:14:in `each_with_index'
./features/step_definitions/entry_steps.rb:14
(eval):3:in `each_with_index'
./features/step_definitions/entry_steps.rb:13:in `each'
./features/step_definitions/entry_steps.rb:13:in `/^I should see the following entries:$/'
features/manage_entries.feature:25:in `Then I should see the following entries:'
2 scenarios (1 failed, 1 passed)
11 steps (1 failed, 10 passed)
0m0.884s
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1....]
(See full trace by running task with --trace)
削除をコメントアウトしたので、削除の部分が失敗しているのがわかると思います。
このような感じでテストを書くことができるようになります。
英語だと若干わかりづらいのですが、有志の手によって一部を日本語で書けるようにしたものもあるようです(今回は省略します)。また、同時に生成されたstepファイル(今回だとentry_steps.rb)で、細かい実行内容の定義を書くことができます。これらを組み合わせることにより、自然な日本語でテストを書くことができるようにもなります。
従来のテストコードに比べて視認性が格段に良くなりますので、Railsで開発をしている方は是非試してみてください。
satoです。
今週末に札幌でオープンソースカンファレンス2009 Hokkaidoがあります。
私もRedmineについて話す予定ですので、北海道の方は是非お越し下さい。
さて、Redmineを普通に使うには、Redmine.jpという、日本語でRedmineの情報を集めたサイトがありますので、こちらをご覧頂けると大体問題なく使えるのですが、実運用になると何かと問題が発生します。
Redmine.jpではPassengerを使用した方法が書かれていますが、Passengerは動作が安定しないことがあり、個人的にはまだ実用するには不安が残ります。
そこで、Railsアプリ運用では一般的だと思われる、mod_proxy_balancerとThinを組み合わせた運用方法について説明します。
WEBrick(素の状態でruby script/serverで起動するサーバ)のRedmineの起動ができていることを前提とします。
■Thin
Thinは、RubyのWebサーバで、Mongrelを内部でパーサとして使用しており、かつMongrelより高速なのが特徴です。
まずインストールをします(以下ほとんどsudo等の管理者権限で実行することになります)
これだけでThinを使うことができます。
なお、Ubuntuで
のようなエラーが出た場合は、
として、ruby1.8-devをインストールすると解決します。
これだけでもいいのですが、このままでは起動が面倒です。また、1プロセスしか存在しないため、1プロセスが死ぬと繋がらない状態になります。
Thinには、両方とも解決する機能が搭載されており、起動スクリプトの自動生成と、クラスタ化ができるようになっております。
で、/etc/init.d/にthinが入ります。
ですが、このままだと起動できないので、起動用の設定ファイルを別途出力します。
Thinの起動スクリプトは、/etc/thin/*.ymlを読み込みます。そのため、そこに設定ファイルを出力します。
/path/to/redmine/はRedmineのパス、--servers 3はプロセスを3つ立ち上げる、-e productionはproduction環境で実行する、ということになります。
出力される/etc/thin/redmine.ymlは名前の通りYAMLですので、簡単に修正することができます。使用するポート番号の範囲とかも書かれているので確認しておいてください。
ここまでできたら、
で、Redmineが起動します。必要に応じてchkconfig thin onなどでサーバ起動時に起動するようにしておきましょう。
■mod_proxy_balancer
mod_proxy_balancerは、Apache2.2から導入されたソフトウェアのロードバランサモジュールです。
今回はThinを複数プロセス起動しているので、これを使用して1つのURLでアクセスできるようにします。
大体こんな感じです。
BalancerMemberで、立ち上げたThinのURLをプロセス分記入します。
ロードバランサだけならPoundやNginxとかでもいいのですが、通常Redmineだけでサーバ1つ占有することはないと思いますので、大体入っていると思われるApacheを使って設定するのが便利だと思います。
■余談
リポジトリ画面は、そのままだと毎回取得して遅いので、本番環境では必ずcronで取得するようにしましょう。
詳細はRedmine.jpの記事をご覧下さい。
今週末に札幌でオープンソースカンファレンス2009 Hokkaidoがあります。
私もRedmineについて話す予定ですので、北海道の方は是非お越し下さい。
さて、Redmineを普通に使うには、Redmine.jpという、日本語でRedmineの情報を集めたサイトがありますので、こちらをご覧頂けると大体問題なく使えるのですが、実運用になると何かと問題が発生します。
Redmine.jpではPassengerを使用した方法が書かれていますが、Passengerは動作が安定しないことがあり、個人的にはまだ実用するには不安が残ります。
そこで、Railsアプリ運用では一般的だと思われる、mod_proxy_balancerとThinを組み合わせた運用方法について説明します。
WEBrick(素の状態でruby script/serverで起動するサーバ)のRedmineの起動ができていることを前提とします。
■Thin
Thinは、RubyのWebサーバで、Mongrelを内部でパーサとして使用しており、かつMongrelより高速なのが特徴です。
まずインストールをします(以下ほとんどsudo等の管理者権限で実行することになります)
gem install rack
gem install thin
なお、Ubuntuで
extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
sudo aptitude install ruby1.8-dev
これだけでもいいのですが、このままでは起動が面倒です。また、1プロセスしか存在しないため、1プロセスが死ぬと繋がらない状態になります。
Thinには、両方とも解決する機能が搭載されており、起動スクリプトの自動生成と、クラスタ化ができるようになっております。
thin install
ですが、このままだと起動できないので、起動用の設定ファイルを別途出力します。
thin config -C /etc/thin/redmine.yml -c /path/to/redmine/ --servers 3 -e production
Thinの起動スクリプトは、/etc/thin/*.ymlを読み込みます。そのため、そこに設定ファイルを出力します。
/path/to/redmine/はRedmineのパス、--servers 3はプロセスを3つ立ち上げる、-e productionはproduction環境で実行する、ということになります。
出力される/etc/thin/redmine.ymlは名前の通りYAMLですので、簡単に修正することができます。使用するポート番号の範囲とかも書かれているので確認しておいてください。
ここまでできたら、
/etc/init.d/thin start
■mod_proxy_balancer
mod_proxy_balancerは、Apache2.2から導入されたソフトウェアのロードバランサモジュールです。
今回はThinを複数プロセス起動しているので、これを使用して1つのURLでアクセスできるようにします。
<VirtualHost *:80>
ServerAdmin admin@example.com
ServerName redmine.example.com
ProxyPass / balancer://redmine_cluster/
ProxyPassReverse / balancer://redmine_cluster/
<Proxy balancer://redmine_cluster/>
BalancerMember http://127.0.0.1:3000 loadfactor=20
BalancerMember http://127.0.0.1:3001 loadfactor=20
BalancerMember http://127.0.0.1:3002 loadfactor=20
</Proxy>
</VirtualHost>
大体こんな感じです。
BalancerMemberで、立ち上げたThinのURLをプロセス分記入します。
ロードバランサだけならPoundやNginxとかでもいいのですが、通常Redmineだけでサーバ1つ占有することはないと思いますので、大体入っていると思われるApacheを使って設定するのが便利だと思います。
■余談
リポジトリ画面は、そのままだと毎回取得して遅いので、本番環境では必ずcronで取得するようにしましょう。
詳細はRedmine.jpの記事をご覧下さい。
satoです。
以前に、MySQLのストアドプロシージャの使い方を紹介しましたが、MySQL5.0では他にもトリガが実装されています。
■トリガとは
トリガ(trigger)とは、名前の通り、何らかの条件によって実行されるイベントのことです。
例えば、「ユーザーをINSERTしたときに特定のグループメンバーにもINSERTする」のようなことをRDBMSレベルで行うことができるようになります。
■簡単な例
トリガとは別物ですが、「このテーブルのレコードを削除したときに、外部キーとしてこのテーブルを参照している他のテーブルのレコードも同時に削除する」というのであれば、MySQL4.1から対応しています。
具体的には、
とすると、usersテーブルのレコードを削除すると、そのidが入っているentriesテーブルのレコードも同時に削除されます。
ON UPDATE/DELETEが、”親のテーブルが”更新または削除が行われた時にどうするかというのを指定します。
・RESTRICT:子のテーブルにデータが残っている場合はエラーにする(デフォルト)
・CASCADE:子のテーブルに結果を反映する(削除されたら削除)
・SET NULL:子の該当カラムをNULLにする
なお、外部キー制約自体がMySQLではInnoDBのみの機能なので、InnoDB以外では使えません。
■トリガ
この要領で、INSERT、UPDATE、DELETE時に更に複雑な処理をできるようにしたのがトリガです。
上記のことと同じ内容をトリガで行うと、
このようになります。
DELIMITERについては前回の記事を参照してください。
トリガの箇所だけ抜き出すと以下になります。
トリガはCREATE TRIGGER文で作成します。
トリガ名は自由に命名して構いません。
実行タイミングは、BEFOREとAFTERがあり、対象命令が実行される前か後かを指定します。通常は挿入系は後、削除系は前、になると思います。
対象命令は、INSERT、UPDATE、DELETEを指定します。どれかの操作が行われた場合にこのトリガが実行されます。複数指定はできないようです。
対象テーブルは、対象命令が行われるテーブルを指定します。
後は、FOR EACH ROW BEGIN〜END;の間に、ストアドと同様にSQL文を書いていきます。
この際、OLDとNEWという疑似テーブルが既に用意されており、このトリガ操作が処理される前の対象テーブルのレコードの状態がOLD、後の状態がNEWになります。通常は、DELETEの場合はOLD、INSERTの場合はNEWを参照することになります。
トリガは制約とは直接関係しないので、InnoDB以外でも使用することが可能です。
ちなみにトリガを削除する場合は、DROP TRIGGER [トリガ名];で削除が可能です。また、SHOW TRIGGERS;でトリガの一覧を確認することができます。
最近のO/Rマッパーでは、擬似的なトリガを用意していることが多く、RDBMSレベルのトリガを使用することは少ないですが、アプリケーション側を修正したくない場合や、そのような仕組みが存在しない環境での開発においてはトリガは有効な選択肢になります。
ただし、トリガを使用するとアプリケーションのコードを読むだけでは挙動を追跡することができなくなるため、状況に応じて適切に利用するようにしましょう。
以前に、MySQLのストアドプロシージャの使い方を紹介しましたが、MySQL5.0では他にもトリガが実装されています。
■トリガとは
トリガ(trigger)とは、名前の通り、何らかの条件によって実行されるイベントのことです。
例えば、「ユーザーをINSERTしたときに特定のグループメンバーにもINSERTする」のようなことをRDBMSレベルで行うことができるようになります。
■簡単な例
トリガとは別物ですが、「このテーブルのレコードを削除したときに、外部キーとしてこのテーブルを参照している他のテーブルのレコードも同時に削除する」というのであれば、MySQL4.1から対応しています。
具体的には、
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name TEXT
) ENGINE=InnoDB;
CREATE TABLE entries (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB;
とすると、usersテーブルのレコードを削除すると、そのidが入っているentriesテーブルのレコードも同時に削除されます。
INSERT INTO users(name) VALUES('sato');
INSERT INTO users(name) VALUES('ryuji');
SELECT * FROM users;
+----+-------+
| id | name |
+----+-------+
| 1 | sato |
| 2 | ryuji |
+----+-------+
3 rows in set (0.00 sec)
INSERT INTO entries(user_id, title, body) VALUES(1, 'タイトル1', '本文1');
INSERT INTO entries(user_id, title, body) VALUES(1, 'タイトル2', '本文2');
INSERT INTO entries(user_id, title, body) VALUES(2, 'タイトル3', '本文3');
INSERT INTO entries(user_id, title, body) VALUES(2, 'タイトル4', '本文4');
SELECT * FROM entries;
+----+---------+---------------+---------+
| id | user_id | title | body |
+----+---------+---------------+---------+
| 1 | 1 | タイトル1 | 本文1 |
| 2 | 1 | タイトル2 | 本文2 |
| 3 | 2 | タイトル3 | 本文3 |
| 4 | 2 | タイトル4 | 本文4 |
+----+---------+---------------+---------+
4 rows in set (0.01 sec)
DELETE FROM users WHERE id = 1;
SELECT * FROM users;
+----+-------+
| id | name |
+----+-------+
| 2 | ryuji |
+----+-------+
1 row in set (0.00 sec)
SELECT * FROM entries;
+----+---------+---------------+---------+
| id | user_id | title | body |
+----+---------+---------------+---------+
| 3 | 2 | タイトル3 | 本文3 |
| 4 | 2 | タイトル4 | 本文4 |
+----+---------+---------------+---------+
2 rows in set (0.00 sec)
ON UPDATE/DELETEが、”親のテーブルが”更新または削除が行われた時にどうするかというのを指定します。
・RESTRICT:子のテーブルにデータが残っている場合はエラーにする(デフォルト)
・CASCADE:子のテーブルに結果を反映する(削除されたら削除)
・SET NULL:子の該当カラムをNULLにする
なお、外部キー制約自体がMySQLではInnoDBのみの機能なので、InnoDB以外では使えません。
■トリガ
この要領で、INSERT、UPDATE、DELETE時に更に複雑な処理をできるようにしたのがトリガです。
上記のことと同じ内容をトリガで行うと、
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name TEXT
);
CREATE TABLE entries (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL
);
DELIMITER //
CREATE TRIGGER user_delete_trigger BEFORE DELETE ON users
FOR EACH ROW BEGIN
DELETE FROM entries WHERE user_id = OLD.id;
END;
//
DELIMITER ;
INSERT INTO users(name) VALUES('sato');
INSERT INTO users(name) VALUES('ryuji');
SELECT * FROM users;
+----+-------+
| id | name |
+----+-------+
| 1 | sato |
| 2 | ryuji |
+----+-------+
3 rows in set (0.00 sec)
INSERT INTO entries(user_id, title, body) VALUES(1, 'タイトル1', '本文1');
INSERT INTO entries(user_id, title, body) VALUES(1, 'タイトル2', '本文2');
INSERT INTO entries(user_id, title, body) VALUES(2, 'タイトル3', '本文3');
INSERT INTO entries(user_id, title, body) VALUES(2, 'タイトル4', '本文4');
SELECT * FROM entries;
+----+---------+---------------+---------+
| id | user_id | title | body |
+----+---------+---------------+---------+
| 1 | 1 | タイトル1 | 本文1 |
| 2 | 1 | タイトル2 | 本文2 |
| 3 | 2 | タイトル3 | 本文3 |
| 4 | 2 | タイトル4 | 本文4 |
+----+---------+---------------+---------+
4 rows in set (0.01 sec)
DELETE FROM users WHERE id = 1;
SELECT * FROM users;
+----+-------+
| id | name |
+----+-------+
| 2 | ryuji |
+----+-------+
1 row in set (0.00 sec)
SELECT * FROM entries;
+----+---------+---------------+---------+
| id | user_id | title | body |
+----+---------+---------------+---------+
| 3 | 2 | タイトル3 | 本文3 |
| 4 | 2 | タイトル4 | 本文4 |
+----+---------+---------------+---------+
2 rows in set (0.00 sec)
このようになります。
DELIMITERについては前回の記事を参照してください。
トリガの箇所だけ抜き出すと以下になります。
CREATE TRIGGER user_delete_trigger BEFORE DELETE ON users
FOR EACH ROW BEGIN
DELETE FROM entries WHERE user_id = OLD.id;
END;
トリガはCREATE TRIGGER文で作成します。
CREATE TRIGGER [トリガ名] [実行タイミング] [対象命令] ON [対象テーブル]
トリガ名は自由に命名して構いません。
実行タイミングは、BEFOREとAFTERがあり、対象命令が実行される前か後かを指定します。通常は挿入系は後、削除系は前、になると思います。
対象命令は、INSERT、UPDATE、DELETEを指定します。どれかの操作が行われた場合にこのトリガが実行されます。複数指定はできないようです。
対象テーブルは、対象命令が行われるテーブルを指定します。
後は、FOR EACH ROW BEGIN〜END;の間に、ストアドと同様にSQL文を書いていきます。
この際、OLDとNEWという疑似テーブルが既に用意されており、このトリガ操作が処理される前の対象テーブルのレコードの状態がOLD、後の状態がNEWになります。通常は、DELETEの場合はOLD、INSERTの場合はNEWを参照することになります。
トリガは制約とは直接関係しないので、InnoDB以外でも使用することが可能です。
ちなみにトリガを削除する場合は、DROP TRIGGER [トリガ名];で削除が可能です。また、SHOW TRIGGERS;でトリガの一覧を確認することができます。
最近のO/Rマッパーでは、擬似的なトリガを用意していることが多く、RDBMSレベルのトリガを使用することは少ないですが、アプリケーション側を修正したくない場合や、そのような仕組みが存在しない環境での開発においてはトリガは有効な選択肢になります。
ただし、トリガを使用するとアプリケーションのコードを読むだけでは挙動を追跡することができなくなるため、状況に応じて適切に利用するようにしましょう。
satoです。
思考を整理する手法として、数年前からマインドマップが流行っております。
このスタッフブログでも、過去に何度か紹介しております。
しかし、マインドマップは真面目にやると実はルールが細かく、とっつきにくい部分があります。
普段からアウトラインプロセッサ(Story EditorやNami2000など)は使っていたので、こういうツリー上のメモを平面に展開できる程度があればなーと思っていました。
そこでFrieve Editorというソフトがあります。
これはアイデアプロセッサの一種で(アウトラインプロセッサもアイデアプロセッサの一種ですが)、KJ法と呼ばれる手法に若干近い感じで、カードを並べてリンクさせることによって整理するツールです。
このような図を簡単に作ることができます。
各カードには個別に長文も書けるので、詳しい内容を書きたい場合にもすぐ書くことができます。
主な使い方としては、
・ENTER:兄弟ノードを伸ばす
・Insert:子ノードを伸ばす
・カーソルキー:カード間移動(リンクの関係性に左右されない)
・PageUp/PageDown:距離拡大/縮小
となります。マウスでポチポチ操作してもいいですが、キーボードでさくさく入力したほうが快適です。
マインドマップ的な使い方もできますが、普通のメモ用途に使ったり、資料作成に使ったりと、様々な使い方に対応できると思います。
基本的には、あまり深く考えずに書いていくのがいいと思います。
自動整理をONにしておくとグリグリ動いて面白いですが、スムーズに動かすにはそれなりにスペックが必要になります。
また、Frieve Editorをプレゼンに使うこともできます。先日のCakePHP勉強会で、実際にFrieve Editorを使ってプレゼンをした方もいるようです。
他の勉強会とかでも、マインドマップ系のツールを使ってプレゼンをする人を最近は見かけるようになりました。話の流れが見えるので使いやすいようです。
ちなみにこの作者様は、他に音楽関係の高機能なソフトを公開しており、これらも非常に便利なので、音楽関係に興味のある方は是非試してみてください。
思考を整理する手法として、数年前からマインドマップが流行っております。
このスタッフブログでも、過去に何度か紹介しております。
しかし、マインドマップは真面目にやると実はルールが細かく、とっつきにくい部分があります。
普段からアウトラインプロセッサ(Story EditorやNami2000など)は使っていたので、こういうツリー上のメモを平面に展開できる程度があればなーと思っていました。
そこでFrieve Editorというソフトがあります。
これはアイデアプロセッサの一種で(アウトラインプロセッサもアイデアプロセッサの一種ですが)、KJ法と呼ばれる手法に若干近い感じで、カードを並べてリンクさせることによって整理するツールです。
このような図を簡単に作ることができます。
各カードには個別に長文も書けるので、詳しい内容を書きたい場合にもすぐ書くことができます。
主な使い方としては、
・ENTER:兄弟ノードを伸ばす
・Insert:子ノードを伸ばす
・カーソルキー:カード間移動(リンクの関係性に左右されない)
・PageUp/PageDown:距離拡大/縮小
となります。マウスでポチポチ操作してもいいですが、キーボードでさくさく入力したほうが快適です。
マインドマップ的な使い方もできますが、普通のメモ用途に使ったり、資料作成に使ったりと、様々な使い方に対応できると思います。
基本的には、あまり深く考えずに書いていくのがいいと思います。
自動整理をONにしておくとグリグリ動いて面白いですが、スムーズに動かすにはそれなりにスペックが必要になります。
また、Frieve Editorをプレゼンに使うこともできます。先日のCakePHP勉強会で、実際にFrieve Editorを使ってプレゼンをした方もいるようです。
他の勉強会とかでも、マインドマップ系のツールを使ってプレゼンをする人を最近は見かけるようになりました。話の流れが見えるので使いやすいようです。
ちなみにこの作者様は、他に音楽関係の高機能なソフトを公開しており、これらも非常に便利なので、音楽関係に興味のある方は是非試してみてください。
satoです。
タグクラウドを出力するライブラリはいろいろありますが、Zend FrameworkにもZend_Tag_Cloudというものが比較的最近実装されたようです。
使い方は簡単です。
これだけで、フォントサイズがweightによって変わるタグクラウドのリストが出力されます。
デフォルトだと、ulにZend_Tag_Cloudという名前のクラス名が割り当てられるので、変更したい場合は、
のようにします。
タグクラウドはそんなに頻繁に実装するものではないので、こういうライブラリを積極的に使うと便利です。
ちなみにPEARにもHTML_TagCloudという同等のライブラリが存在するので、好みで利用してください。
タグクラウドを出力するライブラリはいろいろありますが、Zend FrameworkにもZend_Tag_Cloudというものが比較的最近実装されたようです。
使い方は簡単です。
<?php
require_once('Zend/Tag/Cloud.php');
$cloud = new Zend_Tag_Cloud(array(
'tags' => array(
array(
'title' => 'xoops',
'weight' => 50,
'params' => array('url' => '/tag/xoops')
),
array(
'title' => 'php',
'weight' => 5,
'params' => array('url' => '/tag/php')
),
)
));
?>
<html>
<body>
<?php echo $cloud ?>
</body>
</html>
デフォルトだと、ulにZend_Tag_Cloudという名前のクラス名が割り当てられるので、変更したい場合は、
<?php
require_once('Zend/Tag/Cloud.php');
$cloud = new Zend_Tag_Cloud(array(
'tags' => array(
array(
'title' => 'xoops',
'weight' => 50,
'params' => array('url' => '/tag/xoops')
),
array(
'title' => 'php',
'weight' => 5,
'params' => array('url' => '/tag/php')
),
)
));
$cloud->getCloudDecorator()->setHtmlTags(array('ul'=>array('class'=>'hoge')));
?>
<html>
<body>
<?php echo $cloud ?>
</body>
</html>
タグクラウドはそんなに頻繁に実装するものではないので、こういうライブラリを積極的に使うと便利です。
ちなみにPEARにもHTML_TagCloudという同等のライブラリが存在するので、好みで利用してください。
satoです。
(CSSの)borderを角丸にしたい、というのはデザインでよくある要望の1つだと思います。
以前はNifty Corners Cubeを使用していたのですが、条件によって一部ブラウザで表示がおかしくなることがあり、代替ライブラリを探していました。
いくつか調べたところ、DD_roundiesというライブラリを見つけました。
使い方は簡単で、このファイルを読み込んだ後に、
というのを書いていくだけです。
最後のtrueをfalseにしたり、省略したりするとIE専用になるようです。どういう違いがあるのかわかりませんが、一部の紹介記事では省略された状態になっているので、必ずtrueを付けるようにしてください。
セレクタは、普通のスタイルシートの指定と同様の感じで書けます。
角丸量は、
となっております。
2〜3指定がわかりづらいので、全部同じか全部個別指定がいいと思います。
例えば、この記事限定でこのファイルを読み込むようにしてありますので、アドレスバーに、
と入力すると、この記事の上部の「スタッフブログ - DD_roundiesで角丸表現」という箇所の角が丸くなっているのがわかると思います。
MITライセンスを採用しているため、再配布も容易です。テーマやモジュール制作を行っている方も使用してみてはいかがでしょうか。
jQueryを使っている場合であれば、jQueryを使用した角丸ライブラリがいくつかあるようなので、そちらを使ってみるといいかもしれません。いろいろ試してみてください。
(CSSの)borderを角丸にしたい、というのはデザインでよくある要望の1つだと思います。
以前はNifty Corners Cubeを使用していたのですが、条件によって一部ブラウザで表示がおかしくなることがあり、代替ライブラリを探していました。
いくつか調べたところ、DD_roundiesというライブラリを見つけました。
使い方は簡単で、このファイルを読み込んだ後に、
DD_roundies.addRule([セレクタ], [角丸量], true);
最後のtrueをfalseにしたり、省略したりするとIE専用になるようです。どういう違いがあるのかわかりませんが、一部の紹介記事では省略された状態になっているので、必ずtrueを付けるようにしてください。
セレクタは、普通のスタイルシートの指定と同様の感じで書けます。
.class
div.class
div#id a
指定数 | 順序 | 例 | 例の解説 |
---|---|---|---|
1 | 全部 | 10px | 全ての角を10px丸める |
2 | (左上 右下)、(右上 左下) | 10px 5px | 左上と右下を10px、右上と左下を5px丸める |
3 | (左上)、(右上 左下)、(右下) | 10px 5px 3px | 左上を10px、右上と左下を5px、右下を3px丸める |
4 | 左上、右上、右下、左下 | 10px 5px 3px 1px | 左上を10px、右上を5px、右下を3px、左下を1px丸める |
となっております。
2〜3指定がわかりづらいので、全部同じか全部個別指定がいいと思います。
例えば、この記事限定でこのファイルを読み込むようにしてありますので、アドレスバーに、
javascript:DD_roundies.addRule('#rd3blogHeader', '20px', true);
MITライセンスを採用しているため、再配布も容易です。テーマやモジュール制作を行っている方も使用してみてはいかがでしょうか。
jQueryを使っている場合であれば、jQueryを使用した角丸ライブラリがいくつかあるようなので、そちらを使ってみるといいかもしれません。いろいろ試してみてください。
satoです。
前回に引き続きDropboxの活用法の話です。
以前から日記(ブログとかではなく、完全に非公開の日記)を書こうと思っていたのですが、手書きは厳しいのでPCで手軽に書けるツールを探していました。
一時はブログツールで日記を書いていたのですが、ブラウザで文字を書くのはどうも軽快ではないので面倒になってしまいました。「保存」を押さないと保存されないのも面倒です。
しかしオフラインのツールはいつデータが消えるか不安でした。
Dropboxを併用するようにすれば、オフラインのツールでもそのままバックアップが可能なので遠慮無く利用することができます。
必要な点としては
・単体ソフト(Webは重いので不可)
・検索ができる
・Dropboxとの相性がいい(パスが変わっても問題なく動作する)
・自動保存
・タイトルの記入が必須ではない(思考が止まるため)
といった感じです。
いろいろ探してみたところ、
■Osciroi
を使うことにしました。
編集画面が別なので起動直後に編集できないとかという点は若干気になりますが、見た目が綺麗なのと、一通り機能が揃っているという点が気に入りました。
ただし保存パスは初期状態では絶対パスになるので、一度保存してからiniを直接書き換える必要があります。
次点は
■しばやん日記
一通りの機能も揃っていて、パスも解決できていて便利なのですが、見た目が若干堅い感じというのと、ホイールスクロールが効かないという点で断念しました。
■iDailyDiary Free
編集部分が高機能なのですが、全体的に重いのと、パスが解決できないので断念しました。
■PentaDiary
パス関係を満たしていて、すぐ書けて便利なのですが、リサイズの方法がわからないのと、検索ができないという点で断念しました。
■そら日記
一通り機能が揃っていて便利なのですが、リンクフォルダ(日記にファイルを添付するような機能?)だけが絶対パスで指定する必要があり、環境によってパスが変わると動作しなくなるので断念しました。
■ゆらめきのひび
家計簿とか予定とかも書けて、かつシンプルでいいのですが、パスが変わると動作しませんでした。
ちなみに、Webで書きたいという方は、はてなダイアリーのプライベートモードが手軽でいいと思います。
Dropboxがあれば、急なデータ紛失にも対応できるので、皆さんも好みのツールで日記を書いてみてください。
前回に引き続きDropboxの活用法の話です。
以前から日記(ブログとかではなく、完全に非公開の日記)を書こうと思っていたのですが、手書きは厳しいのでPCで手軽に書けるツールを探していました。
一時はブログツールで日記を書いていたのですが、ブラウザで文字を書くのはどうも軽快ではないので面倒になってしまいました。「保存」を押さないと保存されないのも面倒です。
しかしオフラインのツールはいつデータが消えるか不安でした。
Dropboxを併用するようにすれば、オフラインのツールでもそのままバックアップが可能なので遠慮無く利用することができます。
必要な点としては
・単体ソフト(Webは重いので不可)
・検索ができる
・Dropboxとの相性がいい(パスが変わっても問題なく動作する)
・自動保存
・タイトルの記入が必須ではない(思考が止まるため)
といった感じです。
いろいろ探してみたところ、
■Osciroi
を使うことにしました。
編集画面が別なので起動直後に編集できないとかという点は若干気になりますが、見た目が綺麗なのと、一通り機能が揃っているという点が気に入りました。
ただし保存パスは初期状態では絶対パスになるので、一度保存してからiniを直接書き換える必要があります。
次点は
■しばやん日記
一通りの機能も揃っていて、パスも解決できていて便利なのですが、見た目が若干堅い感じというのと、ホイールスクロールが効かないという点で断念しました。
■iDailyDiary Free
編集部分が高機能なのですが、全体的に重いのと、パスが解決できないので断念しました。
■PentaDiary
パス関係を満たしていて、すぐ書けて便利なのですが、リサイズの方法がわからないのと、検索ができないという点で断念しました。
■そら日記
一通り機能が揃っていて便利なのですが、リンクフォルダ(日記にファイルを添付するような機能?)だけが絶対パスで指定する必要があり、環境によってパスが変わると動作しなくなるので断念しました。
■ゆらめきのひび
家計簿とか予定とかも書けて、かつシンプルでいいのですが、パスが変わると動作しませんでした。
ちなみに、Webで書きたいという方は、はてなダイアリーのプライベートモードが手軽でいいと思います。
Dropboxがあれば、急なデータ紛失にも対応できるので、皆さんも好みのツールで日記を書いてみてください。
昨日までCakePHP開発合宿に参加していたsatoです。
オンラインのメモツールとして有名なものにEvernoteがあります。
しかしEvernoteは残念ながらWindows版のクライアントが使いやすいとは言えません。
Web版はそれなりに使えるのですが、サムネイルの日本語が正常に表示されないとか、そもそもWebだから重いとかという問題があります。
他にもオンラインのメモツールはあるのですが、Webだけのものがほとんどなので、ブラウザがあまり好きでない自分にとっては微妙だったりします。
一方フリーソフトのメモツールはいくつか使いやすいのがあるのですが、こちらは逆にオンラインでの保存ができないため、PCが飛ぶとデータが消えてしまいますし、他のPCからデータを呼び出すことができません。
そこで、「自動保存されるメモツール」と「Dropbox」を組み合わせ、軽快にメモを取る環境を作成しました。
今回使ったツールは、
■CatMemoNote
http://www.cc9.ne.jp/~pappara/
■Dropbox
http://www.getdropbox.com/
になります。
Dropboxはご存じの方も多いと思いますが、自動同期してくれるオンラインストレージサービスです。バージョン管理もしてくれるので、共同作業にも使えますし、単純なバックアップ先としても使える便利なサービスです。使い方は既に各地で紹介されているので省略します。
CatMemoNoteは、メモを取るだけのフリーソフトですが、自動保存され、タスクトレイから簡単に呼び出せるので便利です。また、動作も軽快で、見た目もいいので、快適に使うことができます。メモの全文検索が無いのが難点ですが、メモファイル自体は普通のテキストファイルなので、全文検索が必要な方はGoogle Desktop等を併用するといいと思います。
この記事もCatMemoNoteで書いています。
CatMemoNoteは、メモの保存先をタブ毎に任意のフォルダに指定できるので、保存先をDropboxの管理フォルダ(My Dropbox)に設定するだけです。CatMemoNote自体をDropboxに入れておくと、設定とかも共有できて便利です。
ただし、CatMemoNote自体はそういう用途は考慮されていないので、複数人で同じメモを編集とかはできません。Dropbox側でconflict判定されると思います。
一人で複数の環境でメモを書く場合には十分に使えますが、例えば自宅と職場で作業していて、どちらのPCも起動しっぱなしの場合、同じメモを同時に開いていると、最後に作業したほうに上書きされてしまいますので注意が必要です。(作業前に一度メモを開き直せば問題ないです)
もちろん、Windowsのみになってしまいますので、Mac等を導入した場合は別の手段を考える必要があります。Macだけであれば逆にEvernoteで十分なのですが…
Evernoteに比べると機能は落ちてしまいますが、メモ取りは軽快さが重要だと思うので、Evernoteが重いと思う方は是非試してみてください。
オンラインのメモツールとして有名なものにEvernoteがあります。
しかしEvernoteは残念ながらWindows版のクライアントが使いやすいとは言えません。
Web版はそれなりに使えるのですが、サムネイルの日本語が正常に表示されないとか、そもそもWebだから重いとかという問題があります。
他にもオンラインのメモツールはあるのですが、Webだけのものがほとんどなので、ブラウザがあまり好きでない自分にとっては微妙だったりします。
一方フリーソフトのメモツールはいくつか使いやすいのがあるのですが、こちらは逆にオンラインでの保存ができないため、PCが飛ぶとデータが消えてしまいますし、他のPCからデータを呼び出すことができません。
そこで、「自動保存されるメモツール」と「Dropbox」を組み合わせ、軽快にメモを取る環境を作成しました。
今回使ったツールは、
■CatMemoNote
http://www.cc9.ne.jp/~pappara/
■Dropbox
http://www.getdropbox.com/
になります。
Dropboxはご存じの方も多いと思いますが、自動同期してくれるオンラインストレージサービスです。バージョン管理もしてくれるので、共同作業にも使えますし、単純なバックアップ先としても使える便利なサービスです。使い方は既に各地で紹介されているので省略します。
CatMemoNoteは、メモを取るだけのフリーソフトですが、自動保存され、タスクトレイから簡単に呼び出せるので便利です。また、動作も軽快で、見た目もいいので、快適に使うことができます。メモの全文検索が無いのが難点ですが、メモファイル自体は普通のテキストファイルなので、全文検索が必要な方はGoogle Desktop等を併用するといいと思います。
この記事もCatMemoNoteで書いています。
CatMemoNoteは、メモの保存先をタブ毎に任意のフォルダに指定できるので、保存先をDropboxの管理フォルダ(My Dropbox)に設定するだけです。CatMemoNote自体をDropboxに入れておくと、設定とかも共有できて便利です。
ただし、CatMemoNote自体はそういう用途は考慮されていないので、複数人で同じメモを編集とかはできません。Dropbox側でconflict判定されると思います。
一人で複数の環境でメモを書く場合には十分に使えますが、例えば自宅と職場で作業していて、どちらのPCも起動しっぱなしの場合、同じメモを同時に開いていると、最後に作業したほうに上書きされてしまいますので注意が必要です。(作業前に一度メモを開き直せば問題ないです)
もちろん、Windowsのみになってしまいますので、Mac等を導入した場合は別の手段を考える必要があります。Macだけであれば逆にEvernoteで十分なのですが…
Evernoteに比べると機能は落ちてしまいますが、メモ取りは軽快さが重要だと思うので、Evernoteが重いと思う方は是非試してみてください。
【この記事はエイプリルフールのネタです】
satoです。
お知らせでも告知していますが、この度RYUSでは『龍司が如く』をリリースいたしました。
詳細な説明はお知らせのほうを参照してください。
こちらが動画になります。
綿密なロケハンにより秋葉原の街を完全再現しました。
NPCの一般人もまるで生きているかのように動きます。
パッケージイラストも弊社天野の迫力が出てて会心の出来です。
今後もRYUSではXOOPSカスタマイズとコンテンツ制作を平行して行っていく予定ですので、よろしくお願いします。
satoです。
お知らせでも告知していますが、この度RYUSでは『龍司が如く』をリリースいたしました。
詳細な説明はお知らせのほうを参照してください。
こちらが動画になります。
綿密なロケハンにより秋葉原の街を完全再現しました。
NPCの一般人もまるで生きているかのように動きます。
今後もRYUSではXOOPSカスタマイズとコンテンツ制作を平行して行っていく予定ですので、よろしくお願いします。
satoです。
重い処理の進捗を視覚的に表示したい場合、例えばXOOPS Cubeのインストーラのように、進捗を随時表示するようなケースがあります。
Cubeのインストーラのように、文字をそのまま流すだけであれば、出力を随時flush()してあげればよいのですが、例えばプログレスバーのように視覚的に表示したい場合は何かと面倒だったりします。
Zend FrameworkにはZend_ProgressBarという、そのものを行うためのライブラリがあります。
今回はこれとjQuery UIを使ってプログレスバーを実現してみたいと思います。
PHP側(progress.php)は
HTML側は
このようになります。
これだけでプログレスバーが実装できます。
原理としては、iframeでPHP側を読み込むと、PHP側が随時進捗のJavaScriptコードを吐き出すので、それに応じてプログレスバーを動かすことによって、進捗状況を表示しています。
時間のかかる処理をWeb上から行うようなケースには是非使用してみてください。
重い処理の進捗を視覚的に表示したい場合、例えばXOOPS Cubeのインストーラのように、進捗を随時表示するようなケースがあります。
Cubeのインストーラのように、文字をそのまま流すだけであれば、出力を随時flush()してあげればよいのですが、例えばプログレスバーのように視覚的に表示したい場合は何かと面倒だったりします。
Zend FrameworkにはZend_ProgressBarという、そのものを行うためのライブラリがあります。
今回はこれとjQuery UIを使ってプログレスバーを実現してみたいと思います。
PHP側(progress.php)は
<?php
require_once('Zend/ProgressBar.php');
require_once('Zend/ProgressBar/Adapter/JsPush.php');
// 処理だと思われる何か
$items = array('データ新規作成', 'データ更新', 'データ削除', '後処理');
$adapter = new Zend_ProgressBar_Adapter_JsPush(array('updateMethodName' => 'Zend_ProgressBar_Update', 'finishMethodName' => 'Zend_ProgressBar_Finish'));
$progressBar = new Zend_ProgressBar($adapter, 0, count($items));
foreach($items as $key=>$item) {
// 処理を行っていると思われる部分
$progressBar->update($key + 1); // 進捗の度合いを指定する
sleep(2); // テスト用
}
$progressBar->finish();
HTML側は
<html>
<head>
<script type="text/JavaScript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/JavaScript" src="js/jquery-ui-1.7.1.custom.min.js"></script>
<link type="text/css" href="css/smoothness/jquery-ui-1.7.1.custom.css" rel="stylesheet" />
<script type="text/JavaScript">
<!--
$(function() {
$('#progressbar').progressbar({
value: 0
});
});
function Zend_ProgressBar_Update(data)
{
$('#progressbar').progressbar('value', data.percent);
}
function Zend_ProgressBar_Finish()
{
alert('更新が完了しました!');
}
-->
</script>
</head>
<body>
<div id="progressbar"></div>
<iframe src="progress.php" style="left: -1000px; top: -1000px: width: 1px; height: 1px; position: absolute;"></iframe>
</body>
</html>
このようになります。
これだけでプログレスバーが実装できます。
原理としては、iframeでPHP側を読み込むと、PHP側が随時進捗のJavaScriptコードを吐き出すので、それに応じてプログレスバーを動かすことによって、進捗状況を表示しています。
時間のかかる処理をWeb上から行うようなケースには是非使用してみてください。