Hatena::Groupcatalyst

dann@catalyst このページをアンテナに追加 RSSフィード

2008-03-31

dbic

23:25 |  dbic - dann@catalyst を含むブックマーク はてなブックマーク -  dbic - dann@catalyst  dbic - dann@catalyst のブックマークコメント

Catalystはある程度わかったから、自分でたんたんと調べるだけで何とかなる気がしてきました。Catalyst Internalな話も知識は結構前のものだけど、もう1回読み直せばなんとかなりそう。多分、細かいところでいくらでも改善できる余地は残っていると思うのだけれど。

むしろ、DBICでわからないことのほうが圧倒的に多い。DBICについて、この日記上で続けるのがいいのかが少し微妙なところだから、どうするかだなぁ。

mod_perl2系で

23:25 |  mod_perl2系で - dann@catalyst を含むブックマーク はてなブックマーク -  mod_perl2系で - dann@catalyst  mod_perl2系で - dann@catalyst のブックマークコメント

Apache2::RequestUtilでrequest参照して、そこからpnotesに突っ込めばいいのか。

Catalystのプロジェクト用のタグ生成

| 22:55 |  Catalystのプロジェクト用のタグ生成 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystのプロジェクト用のタグ生成 - dann@catalyst  Catalystのプロジェクト用のタグ生成 - dann@catalyst のブックマークコメント

さっきのやつのctags版.

ctagspという名前でPATHの通ったところに保存。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Basename qw(dirname);
use File::Spec::Functions qw(catfile);
use Cwd;

my $basedir = find_module_base();
die "Can't find module base directory\n" unless $basedir;

chdir($basedir) or die "chdir $basedir:$!";

run_ctags();
exit 0; # not reached

sub run_ctags {
    my @ctags = ('ctags', '-R');
    print STDERR "@ctags\n";
    exec(@ctags);
    die "exec failed: $!";
    # not reached.
}

sub find_module_base {
    my $curdir = shift || cwd;
    chomp($curdir);

    while ($curdir && $curdir ne '/') {
        return $curdir if looks_like_base($curdir);
        $curdir = dirname $curdir; # try ../
    }
    return; # not found.
}

sub looks_like_base {
    my $dir = shift;

    return ( -d catfile($dir, 't')
            && -e catfile($dir, 'README')
            && (-e catfile($dir, 'Makefile.PL')
                || -e catfile($dir, 'Build.PL')));
}

.vimrc

set tags+=./tags;/

~/.vim/ftplugin/perl.vim

noremap ,c <Esc>:!ctagsp<CR>

vim中からprove

| 20:55 |  vim中からprove - dann@catalyst を含むブックマーク はてなブックマーク -  vim中からprove - dann@catalyst  vim中からprove - dann@catalyst のブックマークコメント

emacsからprove

http://saltyduck.blog12.fc2.com/blog-entry-23.html

おぉ、そうかモジュールのディレクトリっぽいところを上に辿ってけばいいのか。vimだけでやること考えていたけど、この方法でいいですね。

/usr/local/bin/perlを/usr/bin/envに変えたのと、looks_like_baseの判定を緩くしました。

以下のコマンドをprvでPATHの通ったところに保存

#!/usr/bin/env perl
use strict;
use warnings;
use File::Basename qw(dirname);
use File::Spec::Functions qw(catfile);
use Cwd;

my @test_files = @ARGV;
@test_files = qw(t) unless @test_files;

my $basedir = find_module_base();
die "Can't find module base directory\n" unless $basedir;

chdir($basedir) or die "chdir $basedir:$!";

run_prove();
exit 0; # not reached

sub run_prove {
    my @prove = ('prove', '-vl', @test_files);
    print STDERR "@prove\n";
    exec(@prove);
    die "exec failed: $!";
    # not reached.
}

sub find_module_base {
    my $curdir = shift || cwd;
    chomp($curdir);

    while ($curdir && $curdir ne '/') {
        return $curdir if looks_like_base($curdir);
        $curdir = dirname $curdir; # try ../
    }
    return; # not found.
}

sub looks_like_base {
    my $dir = shift;

    return ( -d catfile($dir, 't')
            && -e catfile($dir, 'README')
            && (-e catfile($dir, 'Makefile.PL')
                || -e catfile($dir, 'Build.PL')));
}

~/.vim/ftplugin/perl.vimに以下を追加。

noremap ,t <Esc>:!prv -lv t/%<CR>

以下で書いていた問題は解決した。

http://catalyst.g.hatena.ne.jp/dann/20080330/1206892065

後はプロジェクトのディレクトリのtagの更新もこれベースでいける。vimで更新されたtagを読むにはどうすればいいかだけ調べないと。

vimをIDEっぽく使う (autocomplpop + ctags)

| 18:53 |  vimをIDEっぽく使う (autocomplpop + ctags) - dann@catalyst を含むブックマーク はてなブックマーク -  vimをIDEっぽく使う (autocomplpop + ctags) - dann@catalyst  vimをIDEっぽく使う (autocomplpop + ctags) - dann@catalyst のブックマークコメント

LLを使ううえで大変なことは、Eclipseがないことに尽きる。補完がしにくいし、ソースコードjumpがしにくい。けれど、これはautocomplpopとctagsを組み合わせると、Eclipseまで完璧ではないもののそれに近いことが実現できる。

  • ctags+vimで該当箇所へのjump
    • ポイントは、使うモジュールの全部のタグを作っておいて、cronでまわして生成しておくこと
    • ただし、Eclipseは型がわかっているから、一発で該当箇所にjumpできるけど、LLの場合だとそこまで完璧にはいかない。
    • Eclipseを使っていたときもJavaDoc(HTML)は使わないで、ライブラリのソースコードをattachしてEclipse上でソースコードをjumpしながら読んでいた。それに近い感じ。
  • autocomplpopでtagとbufferから補完
    • これで、がんがん補完できる。autoで補完候補がでるのが肝。

タグは以下のようなスクリプトをcronでまわしてタグを作っている。

#!/bin/sh

echo 'Generating tags for perl ...'
ctags -f ~/.vim/tags/perl/cpan.tags --recurse --totals \
    --exclude=blib \
    --exclude=.svn \
    --exclude='*~' \
    --languages=Perl --langmap=Perl:+.t \
    /usr/local/share/perl/5.8.8/

echo '\nGenerating tags for ruby ...'
ctags -f ~/.vim/tags/ruby/gems.tags --recurse --totals \
    --exclude=blib \
    --exclude=.svn \
    --exclude='*~' \
    --languages=Ruby \
    /var/lib/gems/1.8/gems/

ThaBymnThaBymn2018/02/17 00:54Buy Generic Propecia Online Alli Diet Pill Mexico Does Cephalexin Work On Nail Infections <a href=http://cial10mg.com>online pharmacy</a> Buy Cheap Lasix Online Generic Macrobid On Sale Canada Mastercard Acceptedglucophage Zithromax Suspension Storage

LesEntilaLesEntila2018/04/16 22:20Giorni Alterni Finasteride Propecia Medsforless <a href=http://brandciali.com>buy generic cialis</a> Cheapest Fastest Canadian Pharmacy Levitra Ersatz

EllroyatoEllroyato2018/04/21 01:09Strep Throat 200ml Of Amoxicillin Donde Comprar Cialis Foro Buy Estrofem <a href=http://brandciali.com>generic cialis from india</a> Cephalexin Dosage For Sinus Infection Secure Ordering Free Shipping Dutasteride Ups Medicine Amex Accepted

JosephlycleJosephlycle2018/05/09 06:32Write My Essay - EssayErudite.com

Fed up of typing "who can write my essay" in the search bar?
Would you like to have a reliable helper always by your side?
Our website will come as an excellent solution to <a href=https://essayerudite.com/write-my-essay/>write my essay</a> for everyone!

<a href="https://essayerudite.com/write-my-essay/" />write my essay</a>

Write My Essay - https://essayerudite.com/write-my-essay/

DennisAbathDennisAbath2018/05/09 07:36Do My Homework - EssayErudite.com

I can't <a href=https://essayerudite.com/do-my-homework/>do my homework</a> alone - that is not a problem anymore. EssayErudite.com will always be by your side whenever you call for writing help.
EssayErudite represents a pool of native-speaking editors, proofreaders, instructors and writers to handle any writing task by the deadline.

I need assistance to <a href="https://essayerudite.com/do-my-homework/" />do my homework</a>

Do My Homework - https://essayerudite.com/do-my-homework/

TylerCremaTylerCrema2018/05/09 22:51Essay Writing Service - EssayErudite.com

Our <a href=https://essayerudite.com>essay writing service</a> provides a full-scale writing assistance accessible online 24/7.
Apart from many other writing companies, we are rather picky when forming a professional staff of experts.
Moreover, we try to make our every customer feel safe and pleased with the service.

<a href="https://essayerudite.com" />essay writing service</a>

Essay Writing Service - https://essayerudite.com

HaroldfupHaroldfup2018/05/10 00:01Write My Essay For Me - EssayErudite.com

Can You <a href=https://essayerudite.com/write-essay-for-me/>Write Essay For Me</a> ? How many times do students worldwide ask this question without finding an answer?
We'd rather not check the official statistics. At the same time, essays appear to be probably among the most popular academic assignments.
Students from various education establishments need to complete them constantly. If you are among those lucky ones, feel free to contact our customer service department and opt for an essay writing help.

<a href="https://essayerudite.com/write-essay-for-me/" />Write Essay For Me</a>

Write My Essay For Me - https://essayerudite.com/write-essay-for-me/

CharlespawCharlespaw2018/05/10 00:29Thesis Writing Service - EssayErudite.com

If you look for a trustworthy <a href=https://essayerudite.com/thesis-writing-service/>thesis writing service</a> and want to benefit from a higher grade, your editors, proofreaders, and instructors are here to lend you a hand.
Some students afraid of hiring professional writers due to ethical issues. As a result, they fail the course due to various reasons not able to defend their degree.
You should note that there is nothing wrong with opting for a <a href="https://essayerudite.com/thesis-writing-service/" />thesis writing service</a>. EssayErudite is certainly the best place for that.

Thesis Writing Service https://essayerudite.com/thesis-writing-service/

JustinshorpJustinshorp2018/05/13 19:28Write My Paper - EssayErudite.com

Looking for an expert to <a href=https://essayerudite.com/write-my-paper/>write my paper</a> for you? You are at the right place.
Providing superior writing service appears to be our main specialization and passion.
Our website is the best destination for every English-speaking student who calls for assistance when handling his or her daily academic tasks.

<a href="https://essayerudite.com/write-my-paper/" />write my paper</a>

Write My Paper - https://essayerudite.com/write-my-paper/

WilliamwemWilliamwem2018/05/13 20:19Buy Essay - EssayErudite.com

Where to <a href=https://essayerudite.com/buy-essay/>buy essay</a> online ? Our experienced writers can boast higher degrees in addition to exceptional writing skills.
You now have a great chance to buy essay papers online with only a couple of clicks.

<a href="https://essayerudite.com/buy-essay/" />Buy Essay</a>

Buy Essay Online - https://essayerudite.com/buy-essay/

HenryskitoHenryskito2018/05/13 21:42Paper Writing Service - EssayErudite.com

We value excellent academic writing and strive to provide outstanding <a href=https://essayerudite.com>paper writing services</a> each and every time you place an order. We write essays, research papers, term papers, course works, reviews, theses and more, so our primary mission is to help you succeed academically.

Don't waste your time and order our <a href="https://essayerudite.com/" />paper writing service</a> today!

Best Essay Paper Writing Service -https://essayerudite.com

SEOWRITHSEOWRITH2018/05/15 09:46
http://seorussian.ru - ceo продвижение - http://seorussian.ru - seorussian.ru

GuestAremaGuestArema2018/05/19 21:07guest test post
<a href=" http://temresults2018.com/ ">bbcode</a>
<a href="http://temresults2018.com/">html</a>
http://temresults2018.com/ simple

HelenNubHelenNub2018/05/21 03:09SEO / SMMソフトウェア「XRumer 16.0 + XEvil 4.0」の革新的なアップデート:
Google(ReCaptcha-2とReCaptcha-3)、Facebook、BitFinex、Bing、Hotmail、SolveMedia、Yandex、
8400以上の別のタイプのキャプチャ、
最高精度(80..100%)と最高速度(100μg/秒)を備えています。
iMacros、XRumer、GSA SER、ZenoPoster、Srapebox、Senukeなど100以上の他のプログラムで、XEvil 4.0を最も人気のあるSEO / SMMソフトウェアで使用できます。

興味がある? YouTubeにはXEvilに関するビデオが多数紹介されています。
じゃあまたね ;)

DerbnedoDerbnedo2018/05/21 17:28E come in questo caso si deve fare?
<a href="http://window.org/forum/viewtopic.php?f=5&t=8722">descargar videos pornos de tias follando con sus sobrino</a>
<a href="http://www.ffna-network.com/Webboard/showthread.php?tid=545&pid=31557#pid31557">yeni-luv</a>
<a href="http://succubusforums.com/showthread.php?tid=808&pid=7988#pid7988">home naked model</a>
<a href="http://zhcloud.win/forum.php?mod=viewthread&tid=8033&extra=">free nude fucking video clips</a>

hasfesihaphasfesihap2018/05/30 23:35[url=http://doxycycline-cheapbuy.site/]doxycycline-cheapbuy.site.ankor[/url] <a href="http://onlinebuycytotec.site/">onlinebuycytotec.site.ankor</a> http://tadalafilcialis-cheapestprice.site/

ubudejsoyubudejsoy2018/05/31 00:12[url=http://doxycycline-cheapbuy.site/]doxycycline-cheapbuy.site.ankor[/url] <a href="http://onlinebuycytotec.site/">onlinebuycytotec.site.ankor</a> http://tadalafilcialis-cheapestprice.site/

DenGurLowDenGurLow2018/05/31 20:38[img]http://partnerkalab.ru/wp-content/uploads/2017/09/rb2.jpg[/img]

[b]Приветственный бонус в размере $30 начисляется после выполнения нескольких простых действий:[/b]

1 Верификация персональных данных- включает необходимость верификации действующего паспорта и реального адреса.
2 Верификация личного телефонного номера.
3 Пополнение торгового счета собственными средствами на сумму $10. Клиент имеет право, по мере необходимости, вывести эти средства.

Исходя из типа выбранного торгового счета, размер приветственного бонуса составляет $30 либо 3000 Cent. Бонус может зачисляться и в евро. В этом случае, по внутреннему курсу брокерской компании будет произведена конвертация. Клиент компании имеет право на единоразовое получение данного приветственного бонуса. Бонус доступен на платформах MetaTrader 4 и 5 и счетах Fix/Pro-Cent и Fix/Pro-Standard.

[url=http://partnerkalab.ru/roboforex-welcome-bonus-30-usd/]Подробнее как пеолучить 30$[/url]

LindapesLindapes2018/06/04 12:36[center][URL="https://mityr-trans.com"][img]https://mityr-trans.com/wp-content/themes/mitur/images/object2065041431.jpg[/img][/URL][/center]
[center][b][size=20][color=red]Наша компания оказывает услуги международных грузоперевозок на Ваших условиях !!![/color][/size][/b][/center]
[center][b][size=20][color=red]Максимальные гарантии !!![/color][/size][/b][/center]
[center][b][size=20][color=red]Кратчайшие сроки !!![/color][/size][/b][/center]
[center][b][size=20][color=red]Лучшие цены !!![/color][/size][/b][/center]
[center][b][size=20][color=blue]Основные маршруты по/из РФ - Омск, Томск, Челябинск, Екатеринбург, Новосибирск, Красноярск, Тюмень, Пермь[/color][/size][/b][/center]



[center][URL="https://mityr-trans.com"][img]https://mityr-trans.com/wp-content/themes/mitur/images/logo-1996480367.png[/img][/URL][/center]















международные грузоперевозки РФ РБ
недорогие международные грузоперевозки Томск отзывы
недорогие международные грузоперевозки Пермь отзывы
недорогие международные грузоперевозки Россия Беларусь
международные грузоперевозки Томск
недорогие международные грузоперевозки отзывы
недорогие международные грузоперевозки Красноярск отзывы
недорогие грузоперевозки Челябинск отзывы
недорогие международные грузоперевозки Омск
международные грузоперевозки Томск отзывы
международные грузоперевозки Новосибирск отзывы
международные грузоперевозки Красноярск
недорогие грузоперевозки Новосибирск
недорогие грузоперевозки Тюмень
недорогие грузоперевозки РФ РБ
международные грузоперевозки Красноярск отзывы
грузоперевозки Омск отзывы
международные грузоперевозки Пермь отзывы
недорогие международные грузоперевозки РФ РБ отзывы
международные грузоперевозки Россия Беларусь отзывы

LiftekgroumLiftekgroum2018/06/06 00:07Если ищите [b]качественное[/b] аварийное и техническое [b]обслуживание лифтов[/b] в Брянске. [b]Если требуется ремонт[/b] лифта в жилом доме, в торгово-офисном или промышленном здании - приглашаем ознакомится с информацией по теме на сайте
http://liftek32br.wordpress.com
Диспетчеризация лифтов современным оборудованием. Модернизация.
Всё на высшем уровне. Заходите

[url=https://liftekbr.wordpress.com/2018/05/03/%d0%bf%d0%b5%d1%80%d0%b2%d0%b0%d1%8f-%d0%b7%d0%b0%d0%bf%d0%b8%d1%81%d1%8c-%d0%b2-%d0%b1%d0%bb%d0%be%d0%b3%d0%b5/]https://liftekbr.wordpress.com/2018/05/03/%d0%bf%d0%b5%d1%80%d0%b2%d0%b0%d1%8f-%d0%b7%d0%b0%d0%bf%d0%b8%d1%81%d1%8c-%d0%b2-%d0%b1%d0%bb%d0%be%d0%b3%d0%b5/[/url]

bakuninatheldbakuninatheld2018/06/08 07:24[img]http://partnerkalab.ru/wp-content/uploads/2017/12/Kryptex_mayning-520x245.jpg[/img]

[b]Кryptex[/b] — абсолютно бесплатная программа, позволяющая заработать криптовалюту.
Данная программа отличается от большинства сервисов для майнинга!

Во-первых, она бесплатная, и это не сервис для клауд-майнинга поскольку здесь многое зависит от мощности компьютера, чем выше производительность компьютера — тем больше можно заработать, а если подключить ферму, то доход будет очень серьёзный.
Во-вторых, разработчики предусмотрели вывод денег множеством способов и даже на прямую на банковскую карточку. Среди прочего есть достаточно привычные: Qiwi, Яндекс деньги и Paypal, вывод минималки 50 рублей.

[b]Воспользоваться сервисом очень просто:[/b]

1. нужно скачать приложение;
2. [url=http://bidfx.ru/www.kryptex.org]зарегистрироваться[/url]
3. включить программу;
4. получать выплаты.

Таким образом минимально можно заработать 1500 рублей, получать выплаты при этом можно в биткоинах, рублях, тенге или гривнах.
Более подробно как настроить Кryptex, и повысить свою прибыль [url=http://bidfx.ru/kryptex]читайте тут[/url]

StephenmalStephenmal2018/06/08 08:37Новый маркетинг план LifePharm Global Network 2018, Ламинин, Чудо Здоровья и Молодости, laminine LPGN. Laminine LPGN, LifePharm Global Network предлагает Новый маркетинговый (компенсационный) план с 1 апреля 2018. Но Уже есть аналог, дешевле в 4 раза. Смотрите на сайте http://1541.ru

[url=http://1541.ru]AminoBoosters единственный в Мире натуральный продукт из оплодотворенного куриного яйца, способный спасать тяжело больных с самыми разными заболеваниями тогда, когда медицина бессильна. Дешевле ламинина в 4 раза[/url]

[url=http://1541.ru]Only for USA in Russian Analogue of Laminine AminoBoosters (aminopure) are 4 times more affordable as Laminine by LPGN[/url]

[url=http://1541.ru]Заработок без вложений, без обяз. ежемесячных покупок и Асолютно без приглашений, если пожелаете. Скайп evg7773 Работаем с Аналогом Ламинина 1 в 1, но дешевле чем Laminine LPGN в 4 раза. Продукт спасает, Когда медицина помочь не Может априори[/url]

wendypw1wendypw12018/06/09 20:40 Hi reborn project
http://muslim.clit.pornpost.in/?post-armani
queensland muntari companies industries first

AgustinAloraAgustinAlora2018/06/10 04:35cbd pills [url=https://cbdpillsforsale.org/]best cbd oil for pain management[/url]
high cbd cannabis seeds for sale [url=https://cbdpillsforsale.org/]cbd oil ingredients[/url]

miracle cbd cannabis capsules <a href="https://cbdpillsforsale.org/">high cbd low thc cannabis seeds for sale</a>
cannabis seeds high in cbd <a href="https://cbdpillsforsale.org/">pure cbd oil for pain relief</a>

MeganHooryMeganHoory2018/06/12 18:18перевозка грузов томск фото города
услуги транспортных перевозок
екатеринбург газели грузоперевозки
расчет грузоперевозок по россии
авто грузоподъемность тонны
груз груз машина находить перевозка
перевозки по хабаровску
груза перевозка тюмень
открыть грузоперевозки рб
правила грузоперевозок в беларусь

[URL="https://mityr-trans.com"]хабаровск пермь грузоперевозки[/URL]

LiftmndorLiftmndor2018/06/13 23:36Вы полним поставку лифтов, эскалаторов.
Импортное и отечественное подъемное оборудование. В кратчайшие сроки.
Гарантируем качество. Опыт работы 10 лет.
"ЛифтМонтажНаладка" наш сайт liftmn.ru

[url=http://liftmn.ru]http://liftmn.ru[/url]

IsrafaceRooxyIsrafaceRooxy2018/06/14 18:14Знакомства в Израиле бесплатно

[url=http://israface.com]Израиль еврейская социальная сеть и знакомства в израиле подробнее тут[/url]

RomannorRomannor2018/06/15 08:19Иногда сайты в инете создаются не с коммерческой целью, а ради души. И продвигаются они в internet самими создателями. Которые через отсутствие знаний в сео допускают ошибки. Посмотреть их перечень можно в статье [url=http://interesu.ru/index.php/poleznye-sovety/1164-prodvizhenie-v-internete]Продвижение в интернете[/url], а также [url=http://interesu.ru/index.php/poleznye-sovety/888-prodvizhenie-sajtov]Продвижение сайтов[/url].

StephenmalStephenmal2018/06/15 11:30В 2011 году мир узнал о Laminine. Ламинин выпускали в 2011-2012 годах на норвежском экстракте YТЕ (из оплодотворенного куриного яйца).
Создал его д-р Эскеланд в 1994 г. Основа Восстановления, это Экстракт YТE, его количество в 1 капсуле.
И миллионы уже проверили, что этот натуральный ( не лекарство) продукт спасает там, где медицина бессильна.
Ламинин восстанавливает Все органы до здорового состояния.
Все, по очереди. Главное не опоздать.
Главный минус - Дорого, а пить надо много.
А заработать хотя бы $200 в проекте LPGN (а он продавался только в МЛМ) могли единицы.
Сегодня есть Замена ламинину, это AminoBoosters ( AminoPure) 1 в 1, но дешевле в 4 раза на том же неизменном и знаменитом Экстракте YТE от д-ра Эскеланда и в 1 капсуле 400 - 420 мг, а в ламинине 200.
Технологию доктора Эскеланд за все время Никто не повторил!
Вот и работает сегодня Экстракт YТЕ в AminoBoosters намного эффективнее, чем более слабый и дорогой ламинин.

Консультации по скайпу evg7773
Подробно на сайте http://1541.ru

Only for USA in Russian Analogue of Laminine AminoBoosters (aminopure) are 4 times more affordable as Laminine by LPGN http://1541.ru/rz1.html

SergWorldSecurSergWorldSecur2018/06/15 14:47Wer einen Kite sucht, der auch in Sachen Hangtime und Big Air eine gute Figur macht, wird mit dem Catalyst eben so wenig enttauscht werden. Besonders die langen Hangtimeeinlagen haben uns begeistert. Dabei setzt der Catalyst den Kiter immer soft auf dem Wasser wieder ab.
Это же советую
http://wwap.zsko.ru/adress/forum/view_profile.php?UID=104083
http://alexglass-spb.ru/index.php?subaction=userinfo&user=hifaca
http://bhm.com.ua/index.php?subaction=userinfo&user=nadivewo

ThomasWemyThomasWemy2018/06/15 20:52[b][url=https://bit.ly/2Jcmk50]pharmacie en ligne[/url][/b]



[url=https://bit.ly/2Jcmk50][img]http://oi64.tinypic.com/xm8uuf.jpg[/img][/url]



https://bit.ly/2Jcmk50




















cialis livraison rapide forum
vente de cialis en suisse
avis pharmacie en ligne cialis
ou acheter du viagra en algerie
dapoxetine pas chere
prix cialis belgique
acheter cialis maroc
acheter du viagra par cheque
viagra generique livraison rapide
vendre viagra en france
prix du viagra en pharmacie a lyon
comment acheter dapoxetine
acheter cialis 5 milligrams
levitra ou cialis generique
pharmacie viagra pfizer
peut acheter cialis sans ordonnance
prix boite cialis pharmacie
acheter du cialis sur internet forum
forum acheter viagra sans ordonnance
achat cialis 5mg
peut acheter du viagra sans ordonnance
achat cialis site sur
ou se procurer du viagra pour femme
le viagra en generique
tadalafil 5mg prix
cialis 5 mg hatasa

RoyceMekRoyceMek2018/06/16 04:45[url=http://lanatrade.eu/]http://lanatrade.eu/[/url] wougsUnwiliAnowash

IvanCarabIvanCarab2018/06/16 05:49[url=http://prezidentshop.ru/rasprodazha-duhov.html]распродажа духов за 1 рубль[/url] распродажа духов со склада украина

BrettTusBrettTus2018/06/16 07:32wh0cd15749 [url=http://tadalafil777.us.com/]tadalafil sale[/url]

IgnatCoaniIgnatCoani2018/06/16 07:45[url=http://prezidentshop.ru/renumax-sredstvo-dlya-udaleniya-carapin.html]renumax официальный сайт[/url] renumax официальный сайт

BrianWibeBrianWibe2018/06/16 12:17[b][url=https://bit.ly/2LWntPH]best online pharmacy[/url][/b]



[url=https://bit.ly/2LWntPH][img]http://oi63.tinypic.com/28qs1zl.jpg[/img][/url]



https://bit.ly/2LWntPH




















online pharmacies yahoo answers
canada drugs delray beach
rx sunglasses canada
diflucan online canadian pharmacy
emedzone online pharmacy
canadian drug and alcohol rehab
canadian pharmacy xtandi
canada medicene without prescription
best canadian online pharmacy
veterinary compounding pharmacy canada
canada otc drug list
canada pharmacy adipex
complaints about canada drugs online
online pharmacy at walmart.com
canadian pharmacy mirena iud
canadian celebrex online
global rx canada
canada drugs address
directory of canadian health care personnel
canadian pharmacys that accept paypal
prescription drugs identifier canada
online pharmacy ottawa
canada customs rules 24 hours
canada pharmacy online no prescription

WarzenGonWarzenGon2018/06/16 13:16viagra vs cialis online
[url=http://vost.rxviagracan.com]viagra lavitra viagra[/url]
buy generic cialis pills online
<a href=http://next.rxviagracan.com>viagra doses content</a>
- buy cialis black
cialis 20mg side effects register

rickga69rickga692018/06/16 20:44 Free porn pictures
http://brazil.porndairy.in/?private_jaqueline
teenage lesbian paulding water payment sister fuck old men berbatov man u

ArthurSnArthurSn2018/06/17 01:01[b][url=https://bit.ly/2sD1GUE]pharmacie online[/url][/b]



[url=https://bit.ly/2sD1GUE][img]http://oi67.tinypic.com/673vj9.jpg[/img][/url]




https://bit.ly/2sD1GUE



















viagra en ligne belgique
prix cialis en ligne
levitra 10mg prix en pharmacie
cialis achat pharmacie
faut il une ordonnance pour acheter du viagra en pharmacie
cialis achat. forum
pilule de viagra effet
viagra pour homme achat
cialis super active online australia
sildenafil pfizer prix
tarif viagra 100 mg
acheter viagra 150 mg
acheter dapoxetine france
achat viagra espagne
viagra 100mg prix
viagra pfizer livraison rapide
levitra acheter
acheter du viagra doctissimo
acheter cialis en ligne canada
sildenafil win prix maroc

BryanLahBryanLah2018/06/17 01:12<a href="http://medicalcases.eu/where-to-buy-lorazepam-online/">Where to Buy Lorazepam Online?</a>

MichaelWeenoMichaelWeeno2018/06/17 04:39Добрый день! Посоветуйте как загрузить и установить программу, выбрал нужную мне программу и при нажатии кнопки загрузки [url=http://bazasofta.net/internet/23-mozilla-firefox-populyarnyy-brauzer.html]mozilla firefox скачать торрент[/url] ничего не происходит, посоветуйте что сделать ? Извиняюсь если написал не в ту ветку.

RobertvargyRobertvargy2018/06/17 04:42corporate website Jarle Thorsen GLOBAL POWER PTE LTD

w0vvu5b4w0vvu5b42018/06/17 07:03roulette simulator - [url=https://roulettecas.com/]roulette games[/url]
free roulette game 247 <a href=" https://roulettecas.com/ ">play roulette for free</a>
https://roulettecas.com/

IgnatCoaniIgnatCoani2018/06/17 10:23[url=http://prezidentshop.ru/kukla-lol-v-sharike.html]кукла лол оригинал[/url] кукла лол картинки

AudreythopeAudreythope2018/06/17 10:47<a href="http://medicalcases.eu/buy-lorazepam-online-without-prescription/">Buy Lorazepam Online Without Prescription</a>

DouglasMaimaDouglasMaima2018/06/17 10:52<a href="http://medicalcases.eu/buy-lorazepam-online-overnight/">Buy Lorazepam Online Overnight</a>

AlinaRogovaAlinaRogova2018/06/17 11:11

http://eur-style.ru/product/mebel-v-interere-kvartiry-kak-podobrat-pravilno
http://kompbook.ru/index.php?productID=128326
http://esysts.ru/index.php?productID=77897
http://stutorg.ru/index.php?productID=43971

o66x286ro66x286r2018/06/17 13:47roulette game - [url=https://roulettecas.com/]roulette games[/url]
play roulette for free <a href=" https://roulettecas.com/ ">play roulette</a>
https://roulettecas.com/

LeonardaluptLeonardalupt2018/06/17 20:13<a href="http://medicalcases.eu/order-cheap-lorazepam-online/">Order Cheap Lorazepam Online</a>

JuanitaReonyJuanitaReony2018/06/18 04:37<a href="http://medicalcases.eu/how-lorazepam-worked/">How Lorazepam worked?</a>

BrettTusBrettTus2018/06/18 06:20wh0cd150865 [url=http://cialis.us.com/]generic cialis[/url]

DavidPulteHafDavidPulteHaf2018/06/18 14:4220 yr old Amusement Center Manager Tommy Donahey from Windsor, has numerous interests including weather forecasting, Writing and candle making. Has enrolled in a world contiki tour. Is incredibly thrilled specifically about going to Royal Exhibition Building and Carlton Gardens. [url=https://www.spreaker.com/user/rahadelazs]kollagen intensiv[/url].

KevinJatKevinJat2018/06/18 16:10В [url=http://abkids.by/]ABkids[/url] вы сможете взять в аренду понравившуюся развивающую игрушку или другой интересующий вас товар на любой срок и по доступным ценам.
В нашем [url=http://abkids.by/shop/]Каталоге детских товаров[/url] представлены не только самые разнообразные развивающие игрушки ведущих мировых брендов, которые пользуются доверием родителей и радуют малышей во всем мире, но и ингаляторы, увлажнители воздуха, батуты и многое другое.
Также мы рады предложить Вам товары для поддержания фигуры, ведь так трудно найти время на хождение в тренажерный зал, когда дома у Вас подрастает маленький малыш.

С уважением, abkids.by
[url=http://abkids.by/]Прокат детских товаров в Минске[/url]!

SolimokpetSolimokpet2018/06/18 18:50[url=http://www.gosrc.ru/index/8-16730]Анатолий Сморгонский[/url]
http://aba.by/board/tools.php?event=profile&pname=NikodinRog
Анатолий Сморгонский
Анатолий Сморгонский

VaslamipetVaslamipet2018/06/18 21:34[url=http://yrb2.ru/forum/index.php?action=profile;u=229587]Купить лекарства без рецепта[/url]
http://xn----7sbxknpl.xn--p1ai/user/Valinotfrowl/
Купить алпразолам
Купить трамал

rosiesr69rosiesr692018/06/19 04:18My new work:
http://pearl.w.telrock.org

AmandalubAmandalub2018/06/19 13:35<a href="http://ebogdan.eu/how-diazepam-worked/">How Diazepam worked?</a>

AlbertjenAlbertjen2018/06/19 16:48<a href="http://eduface.eu/order-cheap-clonazepam-online/">Order Cheap Clonazepam Online</a>

collectionesopecollectionesope2018/06/19 18:36
[url=https://waterloo-collection.ru/3125/]Настольная медаль[/url] - можете купить на сайте антиквариата [url=https://waterloo-collection.ru]waterloo-collection.ru[/url]

RichardbruffRichardbruff2018/06/19 21:58[url=https://www.bridordefrance.com/traductions/archive/]alternatives to codeine toddler tonsillectomy in adults[/url] - xanax bars effects high, does grapefruit juice effect xanax

IlyaemumpIlyaemump2018/06/19 22:35На днях мониторил материалы сети, и вдруг к своему восторгу заметил отличный вебсайт. Вот смотрите: [url=https://ask-yug.com/objects/mechta/]жилой комплекс мечта краснодар[/url] . Для моих близких этот сайт произвел яркое впечатление. Всем пока!

WalterscubsWalterscubs2018/06/19 23:52excellent web site http://dissertation.j-lo.co/1995-volvo-850-workshop-service-repair-manual-highly-detailed-perfect-for-the-diy-person.pdf

BrettTusBrettTus2018/06/20 05:41wh0cd15749 [url=http://cialisgeneric.us.org/]cialis generic[/url]

JamienupJamienup2018/06/20 11:30<a href="http://gearso.eu/how-klonopin-worked/">How Klonopin worked?</a>

ThomasbumThomasbum2018/06/21 03:57[url=https://cvvshop.ws/cvv-shop-buy-now/]cvv shop download[/url] - n1 cvv shop, cvv-shop.eu.

DavidtopDavidtop2018/06/21 04:00[url=http://pomoshcvsem.info/%D0%B4%D0%B5%D1%8F%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C/]2 помощь[/url] - сейчас помочь, деньги на благотворительность.

WilliamSigWilliamSig2018/06/21 04:21[url=http://7go.su/]автоматическая накрутка просмотров инстаграм[/url] - подписки инстаграм бесплатно, накрутка инстаграм приложение андроид.

DarlenenesDarlenenes2018/06/21 04:22<a href="http://convexx.eu/purchasing-xanax-online/">Purchasing Xanax online</a>

JamespamJamespam2018/06/21 04:54<a href="http://ocalic.eu/how-ambien-worked/">How Ambien worked?</a>

MetalDveriKievMetalDveriKiev2018/06/21 09:21Надо же.
Вы непременно вероятно зачастую своем бизнесе задавали нынешний урок:
"Как утеплить входные китайские металлические двери?"
Решение присутствует и наши сотрудники несомненно поможем вам принять решение сию задачу
http://niti.by/website?ref=http://dveri.buyonline.su
https://www.play.net/bounce/redirect.asp?URL=http://dveri.buyonline.su
http://elpoliglota.com/Otros/_Marco.asp?dir=http://dveri.buyonline.su

KonstantinemumpKonstantinemump2018/06/21 09:59Час назад смотрел контент сети интернет, неожиданно к своему удивлению открыл отличный ресурс. Я про него: [url=https://ask-yug.com/objects/melodiya/]https://ask-yug.com/objects/melodiya/[/url] . Для нас данный сайт показался довольно привлекательным. Всего доброго!

DaniilFalDaniilFal2018/06/21 20:39неодимовые магниты купить в белгороде адрес и [url=http://frenchkurs.ru]http://frenchkurs.ru[/url]

JasonKawJasonKaw2018/06/21 21:21скачать песню в голове столько проблем http://muzzila.ru/

CarlaDowCarlaDow2018/06/21 22:55<a href="http://ocalic.eu/where-to-buy-ambien-online/">Where to Buy Ambien Online?</a>

marilyneq60marilyneq602018/06/22 01:42My up to date trap outline:
http://meredith.w.telrock.org

KevinadvewKevinadvew2018/06/22 03:58<a href="http://duplos.eu/what-is-generic-tramadol/">What is generic Tramadol?</a>

NormanbemNormanbem2018/06/22 06:33Hi there! [url=http://cialissmx.com/#buy-cialis-with-no-prescription]order cialis[/url] beneficial web site.

CarlosabifeCarlosabife2018/06/22 08:49[url=https://eurodocs.cc/]купить паспорт ес за 350 евро[/url] - купить паспорт ес в украине, как получить паспорт ес гражданину россии.

NormanbemNormanbem2018/06/22 10:43Hello! [url=http://cialissmx.com/#buy-cialis-pills]buy cialis cheap[/url] good internet site.

BrettTusBrettTus2018/06/22 13:36wh0cd83307 [url=http://cialis.us.com/]cialis pharmacy[/url]

NormanbemNormanbem2018/06/22 13:41Hi! [url=http://cialissmx.com/#buy-cialis-cheap]buy cialis uk[/url] good site.

JefferysoxJefferysox2018/06/22 15:05<a href="http://enavody.eu/how-zolpidem-worked/">How Zolpidem worked?</a>

NormanbemNormanbem2018/06/22 15:35Hi! [url=http://cialissmx.com/#buy-cialis-medication]buy generic cialis[/url] good website.

HermanoreSsHermanoreSs2018/06/22 16:53
[url=https://www.atlantamotors.ru/remont/kompyuternaya-diagnostika.html/] диагностика volvo XC60 и всех моделей быстро и качественно [/url]

MinniecouthMinniecouth2018/06/22 16:59<a href="http://niezaleznie.eu/how-valium-worked/">How Valium worked?</a>

NormanbemNormanbem2018/06/22 17:00Hi! [url=http://cialissmx.com/#buy-generic-cialis]purchase tadalafil[/url] excellent website.

NormanbemNormanbem2018/06/22 18:13Hi! [url=http://cialissmx.com/#purchase-cialis]buy tadalafil online[/url] great website.

NormanbemNormanbem2018/06/22 19:22Hi there! [url=http://cialissmx.com/#buy-tadalafil]buy cialis online without prescription[/url] very good website.

BrettTusBrettTus2018/06/22 19:40wh0cd150865 [url=http://buyzithromax.us.com/]buy zithromax[/url]

StephenmalStephenmal2018/06/22 19:54Замена ( Аналог) ламинину, это AminoBoosters ( AminoPure) 1 в 1, но дешевле в 4 раза и выпускается Только на знаменитом Экстракте YТE.
В 1 капсуле 400 - 420 мг, а в ламинине 200. Мир узнал о Ламинине в 2011 году, когда его начали выпускать на норвежском экстракте YТЕ из инкубированного оплодотворенного куриного яйца, который создал д-р Эскеланд. Ламинин творил чудеса в восстановлении здоровья там, где медицина ничего не может сделать априори. Название продукта не важно. Главное, что Восстановление ( не лечение) дает именно Экстракт YТE, его количество в 1 капсуле. Это почти панацея, настолько хорош экстракт. Можно не верить, но факты есть факты. И отзывы в интернете говорят об этом. Главный минус Ламинина - Дорого, так как для получения эффекта пить надо МНОГО. А заработать хотя бы $200 в МЛМ Laminine LPGN могут немногие. Пришло время более дешевого и эффективного Aminoboosters ( AminoPure) на том же сырье, тоже д-р Эскеланд, та же технология. И заработок без обяз. ежемесячных покупок, без копейки затрат, без приглашений, на пассиве с 1-го шага доступен любому. Это партнерка.
Консультации по скайпу evg7773
Подробно на сайте http://1541.ru

Analogue of Laminine AminoPure (AminoBoosters) are 4 times more affordable as Laminine by LPGN. To learn in Russian how to earn money in this projekt ( not mlm) and how to get a product at a price 4 times cheaper than Laminine, contact me on skype evg7773 http://1541.ru
English Buy http://1541.ru/rz1.html

Only for USA in Russian Analogue of Laminine AminoBoosters (aminopure) are 4 times more affordable as Laminine by LPGN http://1541.ru/rz1.html

NormanbemNormanbem2018/06/22 22:33Hello! [url=http://cialissmx.com/#purchase-tadalafil]buy cialis with no prescription[/url] very good site.

NormanbemNormanbem2018/06/22 23:37Hi! [url=http://cialissmx.com/#buy-tadalafil]order tadalafil[/url] very good web page.

BrettTusBrettTus2018/06/22 23:46wh0cd218423 [url=http://cialisgeneric.us.org/]cialis us[/url]

NormanbemNormanbem2018/06/23 00:42Hello! [url=http://cialissmx.com/#purchase-cialis]purchase tadalafil[/url] great web site.

NormanbemNormanbem2018/06/23 01:47Howdy! [url=http://cialissmx.com/#purchase-cialis]cialis[/url] beneficial site.

ShaunNoupsShaunNoups2018/06/23 02:36<a href="http://gawston.eu/how-alprazolam-worked/">How Alprazolam worked?</a>

NormanbemNormanbem2018/06/23 02:52Hi! [url=http://cialissmx.com/#buy-cialis-no-prescription]order cialis[/url] good web site.

NormanbemNormanbem2018/06/23 04:01Hello there! [url=http://cialissmx.com/#buy-cialis-pills]where buy cialis[/url] very good web page.

BrettTusBrettTus2018/06/23 04:06wh0cd15749 [url=http://tadalafil777.us.com/]tadalafil[/url]

NormanbemNormanbem2018/06/23 05:07Howdy! [url=http://cialissmx.com/#order-cialis]cialis cheap[/url] good site.

PolinaNokPolinaNok2018/06/23 05:39 little virgin

https://sexxxyteen.blogspot.com
https://webcam011.blogspot.com

NormanbemNormanbem2018/06/23 06:13Hi there! [url=http://cialissmx.com/#buy-cialis-usa]buy cialis with no prescription[/url] beneficial site.

marianauu2marianauu22018/06/23 07:02 Pornographic pictures blog
http://strapon.erolove.in/?tweet-adriana
miss erotic erotic paint erotic horror movies free adult sites

NormanbemNormanbem2018/06/23 07:15Hi there! [url=http://cialissmx.com/#buy-cialis-with-no-prescription]cialis cheap[/url] great web page.

BrettTusBrettTus2018/06/23 08:17wh0cd83307 [url=http://cialisgeneric.us.org/]cialis cost comparison[/url]

NormanbemNormanbem2018/06/23 08:18Hi! [url=http://cialissmx.com/#cialis]purchase cialis online no prescription[/url] good web page.

NormanbemNormanbem2018/06/23 09:26Hello! [url=http://cialissmx.com/#buy-cialis-online]buy cialis cheap[/url] excellent web page.

DorrisglypeDorrisglype2018/06/23 10:23[url=http://www.youtube.com/watch?v=nvV0EQ30eQI]Рюкзак для мамы[/url]
http://www.youtube.com/watch?v=nvV0EQ30eQI
<iframe width="560" height="315" src="https://www.youtube.com/embed/nvV0EQ30eQI?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>

NormanbemNormanbem2018/06/23 10:31Howdy! [url=http://cialissmx.com/#buy-cialis]cialis online[/url] excellent site.

AmbrosemugAmbrosemug2018/06/23 10:55I was looking concerning seven pieces of Textile in Elodea, but two pieces of remainder is never found.
In my PC, it does not earmarks of to appear.
I clich‚ [url=https://coursbitcoineuro.page.tl/]cours bitcoin[/url] an verve to confirm it.
Then it was near here, but is not found. (cf. photograph)

AlinaRogovaAlinaRogova2018/06/23 11:34

http://eur-style.ru/product/osobennosti-granitnyh-pamjatnikov-na-mogilu
http://ozlon.ru/index.php?productID=93939
http://gmline.ru/product/gruzoperevozki-v-voskresensk
http://canax.ru/index.php?productID=100011

NormanbemNormanbem2018/06/23 11:38Hello! [url=http://cialissmx.com/#buy-tadalafil-pills]cialis[/url] beneficial internet site.

JasonNURDYJasonNURDY2018/06/23 12:23require some problems with the currency switcher plugin. Sooner than inaction, USD is in use. I’ve added some products with a evaluation of ‘100’, the [url=https://coursdubitcoin.tumblr.com/]quel est le cours du bitcoin[/url] department store messenger showed ‘$100’. Then I changed the currency to EUR. The only to all intents: The offshoot site seldom shows the deliberate USD appraisal ($106,83) but it should put on the EUR consequence (€100).

BrettTusBrettTus2018/06/23 12:33wh0cd150865 [url=http://cialis.us.com/]Buy Cialis Online[/url]

RobertDiofeRobertDiofe2018/06/23 13:37<a href="http://ebogdan.eu/i-ordered-diazepam-online/">I ordered Diazepam online</a>

NormanbemNormanbem2018/06/23 13:51Howdy! [url=http://cialissmx.com/#buy-cialis-cheap]buy cialis online without prescription[/url] excellent internet site.

NormanbemNormanbem2018/06/23 16:07Hi there! [url=http://cialissmx.com/#buy-tadalafil-pills]buy cialis medication[/url] great web site.

CharityDuhCharityDuh2018/06/23 16:37<a href="http://niezaleznie.eu/i-ordered-valium-online/">I ordered Valium online</a>

BrettTusBrettTus2018/06/23 16:51wh0cd218423 [url=http://buylasix.us.com/]where to buy lasix[/url]

NormanbemNormanbem2018/06/23 17:17Hi there! [url=http://cialissmx.com/#buy-cialis]buy tadalafil online[/url] excellent internet site.

NormanbemNormanbem2018/06/23 18:29Hi there! [url=http://cialissmx.com/#buy-tadalafil]buy cialis cheap[/url] excellent web site.

NormanbemNormanbem2018/06/23 19:43Hello! [url=http://cialissmx.com/#buy-cialis-medication]where buy cialis[/url] beneficial web page.

KevinmugKevinmug2018/06/23 19:55the first reply was good...those are lovely topics.
however it will be better to post them [url=https://www.heritiers.com/cours-crypto-monnaies/bitcoin]cours bitcoin[/url] little by little in order of event and timing.
Also, try associate your forum with popular trends in sport, entertainment, news and more!

NormanbemNormanbem2018/06/23 20:58Howdy! [url=http://cialissmx.com/#purchase-cialis-online-no-prescription]cialis[/url] beneficial website.

NormanbemNormanbem2018/06/23 22:18Hello there! [url=http://cialissmx.com/#tadalafil]buy cialis no prescription[/url] great site.

NormanbemNormanbem2018/06/23 23:40Hi! [url=http://cialissmx.com/#buy-cialis-cheap]purchase tadalafil[/url] very good site.

CharlesdobCharlesdob2018/06/24 00:52ost Live altoghter in the present circumstances!!!

It is on the computer [url=http://coursbitcoin.gamerlaunch.com/]cours bitcoin cours du bitcoin euros[/url] but choose not unenclosed!

So wish be enduring to reinstall again! But appears I desire not be masterful to handle plugins at all. Everytime I try it crashes! I partake of not in the least had these problems with earlier versions and useing XP. I have gush two weeks trying to fetch this to roam well and I take attempt deadlines in a week.

NormanbemNormanbem2018/06/24 01:03Howdy! [url=http://cialissmx.com/#buy-cialis-online-without-prescription]buy cialis pills[/url] excellent internet site.

LouisfeantLouisfeant2018/06/24 01:05<a href="http://gawston.eu/order-cheap-alprazolam-online/">Order Cheap Alprazolam Online</a>

NormanbemNormanbem2018/06/24 02:28Howdy! [url=http://cialissmx.com/#cialis-online]buy cialis uk[/url] good web site.

NormanbemNormanbem2018/06/24 03:54Hi there! [url=http://cialissmx.com/#buy-tadalafil-pills]buy cialis pills[/url] very good web page.

KeisVedKeisVed2018/06/24 04:26Доброго дня! Отличный у вас сайт!

Несколько часов назад в гугле нашел cimr e7z20300 в компании prom electric. Рекомендую!

Удачи!

NormanbemNormanbem2018/06/24 05:20Hi! [url=http://cialissmx.com/#buy-cialis-pills-online]buy generic cialis[/url] good web site.

NormanbemNormanbem2018/06/24 06:43Hello there! [url=http://cialissmx.com/#buy-tadalafil-no-prescription]buy cialis usa[/url] great internet site.

ArthurmarArthurmar2018/06/24 07:43No, if your BS doesn't assess, chances are it is because every note on the BS that is changing from year to year is all things considered not suitably reflected on the CF statement. Here are the widespread steps to checking and troubleshooting your non-balancing mould
1) Agree all your subtotals and totals on the BS are [url=http://inx.lv/6Ky]cours ethereum[/url] adding correctly.
2) Suitable for thru the BS tale principle at a course and support that if the balance in compensation that article changes in any year, that that modifying is reflected on the CF statement. If not, show a clean pair of heels assured you superabundance it washing people's hands of the CFS, match to to working choice items. You accept to last to see accounting rules and classify in the castigate succeed
3) If all the strike items on the BS are being reflected on the CFS, then in check that you take the signs unimpeachable; entirely standard, that CapEx (filthy lucre cater to) is nicely increasing PPE. Assorted errors are correct to impolitic sign.

NormanbemNormanbem2018/06/24 08:06Howdy! [url=http://cialissmx.com/#buy-cialis-usa]purchase tadalafil[/url] beneficial web page.

NormanbemNormanbem2018/06/24 09:30Hi there! [url=http://cialissmx.com/#buy-cialis-cheap]buy tadalafil[/url] great website.

NormanbemNormanbem2018/06/24 10:55Hi there! [url=http://cialissmx.com/#buy-tadalafil]buy cialis cheap[/url] beneficial website.

GraceIsortGraceIsort2018/06/24 10:58<a href="http://infuture.eu/how-to-buy-ativan-online/">How to buy Ativan online?</a>

NormanbemNormanbem2018/06/24 12:20Howdy! [url=http://cialissmx.com/#buy-cialis-with-no-prescription]buy cialis online[/url] good internet site.

NormanbemNormanbem2018/06/24 13:45Hi! [url=http://cialissmx.com/#cialis-cheap]buy generic cialis[/url] good web site.

NormanbemNormanbem2018/06/24 15:10Hi! [url=http://cialissmx.com/#order-cialis]buy cialis online[/url] excellent website.

RomannorRomannor2018/06/24 15:46Иногда блоги в internet создаются не с коммерческой целью, а ради обсуждения каких-то конкретных тем. И продвигаются они в инете самими авторами. Которые через отсутствие знаний в сео допускают ошибки. Ознакомится с ними можно в статье [url=http://interesu.ru/index.php/poleznye-sovety/1164-prodvizhenie-v-internete]Продвижение в интернете[/url], а также [url=http://interesu.ru/index.php/poleznye-sovety/888-prodvizhenie-sajtov]Продвижение сайтов[/url].

NormanbemNormanbem2018/06/24 16:41Howdy! [url=http://cialissmx.com/#buy-cialis-no-prescription]buy tadalafil no prescription[/url] excellent internet site.

NormanbemNormanbem2018/06/24 18:16Hello! [url=http://cialissmx.com/#purchase-tadalafil]buy cialis medication[/url] very good web page.

IonDibIonDib2018/06/24 19:27[url=http://pornoblydstvo.ru/russkiy-zooseks/]зоопорно[/url] watch online
[url=http://pornoblydstvo.ru/russkiy-zooseks/]зоо порно[/url] movie adult see
[url=http://pornoblydstvo.ru/zooseks-s-konem/]порно с конем[/url] zooporn horse
Русское зоо порно с животными онлайн бесплатно смотреть видео русские девушки горячо трахаются с собаками конями жёсткая зоофилия русских
Русский зоосекс видео порно с животными Зоофилия русских смотреть бесплатно зоопорно без вирусов и смс, онлайн качества супер убойные зоо порно ролики с участием российских женщин и мужчин извращенцев

DemoloogeDemolooge2018/06/24 20:50[url=http://pornoved.com/porno_s_zhyvotnymy/]порно с животными[/url] on pornoved
[url=http://pornoved.com/porno_s_zhyvotnymy/]секс с животными[/url] free online
Сайт качественного видео порно с животными . Мы в топе по:порно с животными, порно зоо, зоо порно, секс с животными, зоосекс, зоо секс, zoo porno, порно с животными видео, zooporno, секс с животными видео.

NormanbemNormanbem2018/06/24 21:44Hi there! [url=http://cialissmx.com/#buy-cialis-medication]buy cialis medication[/url] beneficial web page.

NormanbemNormanbem2018/06/24 23:42Hello there! [url=http://cialissmx.com/#tadalafil]purchase cialis online no prescription[/url] beneficial web page.

lohrencelohrence2018/06/25 00:06[url=http://osmi.biz/page/2/]зоопорно[/url]
[url=http://osmi.biz/page/2/]зоо порно[/url]
[url=http://osmi.biz/]секс с животными[/url]
[url=http://osmi.biz/]порно с животными[/url]
смотреть порно с животными бесплатно, онлайн без вирусов. Без регистрации для просмотра уникальных фильмов и роликов с участием диких и домашних животных, качественная съёмка оргий женщин и мужчин с лошадьми и собаками. Извращенное видео для взрослых про зоофилов без смс на osmi.biz

EdwardRhyncEdwardRhync2018/06/25 00:57<a href="http://gawston.eu/what-is-generic-alprazolam/">What is generic Alprazolam?</a>

NormanbemNormanbem2018/06/25 01:52Hello there! [url=http://cialissmx.com/#buy-cialis-pills]buy cialis uk[/url] great web site.

NormanbemNormanbem2018/06/25 04:15Howdy! [url=http://cialissmx.com/#buy-cialis-pills]buy tadalafil no prescription[/url] great website.

TerriImpubTerriImpub2018/06/25 05:07<a href="http://eduface.eu/how-clonazepam-worked/">How Clonazepam worked?</a>

teplomusteplomus2018/06/25 14:44
[url=https://pteplo.ru/shop/product/17850]Система контроля Neptun ProW+ 1/2[/url] - купить в интерне-магазине [url=https://pteplo.ru]pteplo.ru[/url]

SEOWRITHSEOWRITH2018/06/25 18:51 [url=http://seorussian.ru/]Раскрутка сайтов[/url] - [url=http://seorussian.ru]seorussian.ru[/url]

ArnoldTwefsArnoldTwefs2018/06/25 20:26<a href="http://gawston.eu/purchasing-alprazolam-online/">Purchasing Alprazolam online</a>

SEOWRITHSEOWRITH2018/06/26 05:22 [url=http://seorussian.ru/]Раскрутка сайта[/url] - [url=http://seorussian.ru]seorussian.ru[/url]

stroymusstroymus2018/06/26 05:41
[url=http://xn--80arnbgdebbkg5p.xn--p1ai/Krepezh/Perforirovannyy-krepezh1/Montazhnaya-plastina.html?id=2585]Монтажная пластина - Монтажная пластина 40x840мм[/url] - всегда можно купить на нашем сайте интернет-магазина стройматериалов [url=https://xn--80arnbgdebbkg5p.xn--p1ai/]стройпартнёр.рф[/url]

GradyratGradyrat2018/06/26 08:41[url=https://professionalsolutions.eu/ru/]корейская косметика интернет магазин екатеринбург[/url] - купить интернет магазин косметики для волос, косметика из кореи интернет магазин.

asstyxFalasstyxFal2018/06/26 08:50Hello! How can I contact Admin?
fbnty55fe4

DudleyanidaDudleyanida2018/06/26 16:25<a href="http://gawston.eu/i-ordered-alprazolam-online/">I ordered Alprazolam online</a>

KennethWarKennethWar2018/06/27 03:40<a href="http://niezaleznie.eu/buy-generic-valium-online/">Buy Generic Valium Online</a>

DuanePlumeDuanePlume2018/06/27 08:04good website [url=https://cardsdumps.com/]cvv shop[/url]

IsrafaceRooxyIsrafaceRooxy2018/06/27 09:31Еврейская социальная сеть и знакомства в Израиле

[url=http://israface.com]Израиль народы израиля. Знакомства в Израиле подробнее тут[/url]

FrankenDopFrankenDop2018/06/27 13:39Get exactly what you want with custom website: https://sites.google.com/site/hiringgenius

DonaldisozyDonaldisozy2018/06/27 14:27<a href="http://infuture.eu/what-is-generic-ativan/">What is generic Ativan?</a>

StylemusStylemus2018/06/27 16:15
[url=http://rest-style.ru/]повышение квалификации[/url] - подробнее на сайте курсов парикмахеров [url=http://rest-style.ru/]rest-style.ru[/url]

MarkfitMarkfit2018/06/27 16:42Пару минут назад серфил содержимое сети, и вдруг к своему восторгу заметил актуальный вебсайт. Вот гляньте: [url=http://www.vremyaigr.ru/forum/index.php?PAGE_NAME=profile_view&UID=41140]уборка нижний новгород[/url] . Для меня вышеуказанный вебсайт оказал яркое впечатление. Всем пока!

almazburmusalmazburmus2018/06/28 01:25
[url=https://сихем.рф/rezka-kanatnoi-mashinoi3]Резка канатной машиной[/url] - подробнее на сайте алмазного бурения [url=https://сихем.рф/]сихем.рф[/url]

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080331

2008-03-30

DBIC事始め

01:43 |  DBIC事始め - dann@catalyst を含むブックマーク はてなブックマーク -  DBIC事始め - dann@catalyst  DBIC事始め - dann@catalyst のブックマークコメント

DBIC周りの話は何点か有益な話を聞いた気がするんだけど、自分が理解してないから聞いてるだけだったなぁと。何というか話すコンテキストを共有するためには、もうちょっと内部まで理解してないとなかなかつっこめないし、有益な情報も全く理解できないで終わっちゃう。こういうのは、とても勿体なかったなぁと。

id:ZIGOROuさんの記事をざっと読んで、コードを読み始めよう。

http://d.hatena.ne.jp/ZIGOROu/20080328/1206695347

http://d.hatena.ne.jp/ZIGOROu/20080324/1206356215

DBICでトランザクション管理を楽に

01:11 |  DBICでトランザクション管理を楽に - dann@catalyst を含むブックマーク はてなブックマーク -  DBICでトランザクション管理を楽に - dann@catalyst  DBICでトランザクション管理を楽に - dann@catalyst のブックマークコメント

http://svn.art-code.org/public/perl/DBIx-Class-Service/trunk/

zigorou++

あとでゆっくり読ませてもらいまーす。あざーっす。

読んでみた!

http://svn.art-code.org/public/perl/DBIx-Class-Service/trunk/lib/DBIx/Class/ServiceManager.pm

Perlだと、こうやって動的にクラスを生成することもできるんだなぁ。なるほど。EJBとかみたいにDeployment時にProxy生成するのにイメージは近いなぁ。

AUTOLOADでディスパッチングするときにはさみことかしか思いつかなかったけど、こういう方法もあるんだなぁと。他でも応用が利きそうなテクニックだなぁ。

vim中からprove

00:47 |  vim中からprove - dann@catalyst を含むブックマーク はてなブックマーク -  vim中からprove - dann@catalyst  vim中からprove - dann@catalyst のブックマークコメント

vimのautochdirでカレントのディレクトリを移動するようにすると、*.tファイルを開いたときにそのディレクトリに移動してしまって、vim上からprove実行するときにどうすればいいのかがよくわからない。

アプリケーションのルートを環境変数にいれておけば、proveをするのは簡単。だけど、何か解決方法が間違っているような気もする。というのは、vim自身が起動した場所そのものを知っているような気がするから。

helpで何を検索していいのかがよくわからない。ググり方が悪いのかなぁ...

しょうがないからsuspendしてから、prove -lvでテストやってるんだけど、少し効率悪いかなぁと。

Catalystに依存しないDBICのモデル

00:29 |  Catalystに依存しないDBICのモデル - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないDBICのモデル - dann@catalyst  Catalystに依存しないDBICのモデル - dann@catalyst のブックマークコメント

compose_namespace使えばすごい綺麗にかけるんじゃないかと思った。これもあとで。

OpenIDのベストプラクティス

| 00:23 |  OpenIDのベストプラクティス - dann@catalyst を含むブックマーク はてなブックマーク -  OpenIDのベストプラクティス - dann@catalyst  OpenIDのベストプラクティス - dann@catalyst のブックマークコメント

昨日の飲み会でZIGOROuさんから聞いて、なんだか少しわかった気がすると思ったけど今日になったら忘れてる... Blogに是非書いてほしい!

一つだけ印象的で記憶に残っているのは、「IDがURLになるというのが重要」ということ。SocialGraphのような繋がりもURLで表現できるようになるかもしれないし、何か広がりがありそう。

IDがURLになることで、そのURLにアクセスすればその人の情報やり、人の繋がりだったり、連絡手段だったり、連絡手段だけでなく何らかのほうほうで直接連絡する方法だったりと、色々と広がりがでるのかもしれない。

OpenIDでシングルサインオン」ができるいうこと自体には、利用者側からみるとさほど魅力があるとは思えないなぁと思っていたけれど、IDがURLで表現されることで広がる世界は面白そうだなぁと思った。

全部、日記に書いてあるよ!と突っ込まれたので、もう一回読んでみよっと。

Migration

| 00:12 |  Migration - dann@catalyst を含むブックマーク はてなブックマーク -  Migration - dann@catalyst  Migration - dann@catalyst のブックマークコメント

http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Schema/Versioned.pm

多分、これがActive::RecordのMigrationに対応するものになっていくんだろうという話。ただ、誰もまだ使ってないといってた。

Catalyst->DBIC

22:41 |  Catalyst->DBIC - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst->DBIC - dann@catalyst  Catalyst->DBIC - dann@catalyst のブックマークコメント

これからは、DBICのほうを使えるようにコード読んでいくことにする。

catsetupをCodeReposに移動した

15:13 |  catsetupをCodeReposに移動した - dann@catalyst を含むブックマーク はてなブックマーク -  catsetupをCodeReposに移動した - dann@catalyst  catsetupをCodeReposに移動した - dann@catalyst のブックマークコメント

スクリプト

http://coderepos.org/share/browser/lang/perl/misc/catstarter/dann/catsetup.pl

設定ファイルやテンプレート

http://coderepos.org/share/browser/lang/perl/misc/catstarter/dann/sample

設定ファイルやtemplateもsampleとしていれた。まだ、templateのできはいまいちだけど。

ZIGOROuZIGOROu2008/03/31 16:20今度、どこかしらに網羅的に分かりやすいエントリ挙げますー > OpenID

danndann2008/03/31 17:56おぉ。多分、みんな知りたいんじゃないかと!楽しみにしてます。

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080330

2008-03-29

サービスのスケールアウト

13:53 |  サービスのスケールアウト - dann@catalyst を含むブックマーク はてなブックマーク -  サービスのスケールアウト - dann@catalyst  サービスのスケールアウト - dann@catalyst のブックマークコメント

hidedenさんとかlyokatoさんに聞いたスケールアウトさせる際の話はBlogにはかかれてない話だからとてもためなったなぁ。こういうのは、なかなか飲み会とかじゃないと聞けないかもなぁ。

MVCでの分割とスケールアウト

14:00 |  MVCでの分割とスケールアウト - dann@catalyst を含むブックマーク はてなブックマーク -  MVCでの分割とスケールアウト - dann@catalyst  MVCでの分割とスケールアウト - dann@catalyst のブックマークコメント

LMVCとなっても、LとMが密に結合してしまうというのは、かなりありそう。LとMがCatalystから切り離されていれば、結構どうでもいいものかもしれないな。

get_multiを使った場合に綺麗にModelがかけないっていうのは、そうだろうなぁという気はするし綺麗にMを分割するのは難しいかもしれないとも思った。resultset返すのであれば、MとVはくっついちゃうし。後は、マスタースレーブ分割とクラスタリング時の処理。これも、LogicをModelから切り離すのは、実際無理だよなぁと。

これもサービス規模の程度問題なのだろうけど。まぁ、もう少し考えがまとまったら、なんかまとめておきたいな。

プロセス or リクエスト単位でのconnectionの共有

| 13:53 |  プロセス or リクエスト単位でのconnectionの共有 - dann@catalyst を含むブックマーク はてなブックマーク -  プロセス or リクエスト単位でのconnectionの共有 - dann@catalyst  プロセス or リクエスト単位でのconnectionの共有 - dann@catalyst のブックマークコメント

MySQLでconnectionをどれくらいの数まで張ってパフォーマンスが維持できるのかわからないけど、300程度なら全然パフォーマンス上も問題なく動くってことを聞いた。だから、プロセス単位でconnectionを保持しても結構なアクセスをさばけるらしい。

アプリケーションサーバー数が増えていったときに、プロセス数に応じてconnectionを維持する数が増加していくとボトルネックになるといっても、それは割に後の段階なのかもしれないなぁと思った。そういう段階では、リクエスト単位でDBのハンドラを保持することで、最大接続数を抑えられるのかもしれない。

connectionを張る単位

| 13:53 |  connectionを張る単位 - dann@catalyst を含むブックマーク はてなブックマーク -  connectionを張る単位 - dann@catalyst  connectionを張る単位 - dann@catalyst のブックマークコメント

request単位

  • Apacheのpnotesにいれておけばよいというところまで分かった。requestにローカルな変数を作れるのか。やっぱりあった!こないだ、Catalyst::Pluginでうんぬんっていうのは、考え方が間違いだった。

大体、以下のような感じでpnotesにつっこんどけばよさそうだ。

sub dbh {
  my $dbh = Apache->request()->pnotes('dbh');
  if (!$dbh) {
    $dbh = new_dbh();
    Apache->request()->pnotes('dbh', $dbh);
  }
  return $dbh;
}

プロセス単位

  • Apache::DBIと同様に起動時にdbhを保持しておくっていうのはあるのかもしれない

DBIx::Classのコード中では特にこのdbhの保持単位っていうのは、どこに書いてあるのかわからなかった。

リクエスト単位でDBのハンドらを保持する方法は?

| 13:53 |  リクエスト単位でDBのハンドらを保持する方法は? - dann@catalyst を含むブックマーク はてなブックマーク -  リクエスト単位でDBのハンドらを保持する方法は? - dann@catalyst  リクエスト単位でDBのハンドらを保持する方法は? - dann@catalyst のブックマークコメント

connect_info([ sub { DBI->connect(...) } ]);

DBIx::Class::Storage::DBIの_connectメソッド。connect_infoの一番目の引数がコードレファレンスの場合にはそれが実行されるみたいだから、ここでdbhの返しかたを変えれば、リクエスト単位、プロセス単位の切り替えはできるんじゃないかと思った。

sub _connect {
  (snip)
  eval {
    if(ref $info[0] eq 'CODE') {
       $dbh = &{$info[0]}
    }
    else {
       $dbh = DBI->connect(@info);
    }

以下のような感じで書けるってことらしい。connect_infoに渡すcode_ref中に、mod_perlだったらApacheのpnotesから取得するとかいうコードを書いとけばいいのかな。プロセス単位で保持する場合でもここでよさそう。

  # Connect via subref
  ->connect_info([ sub { DBI->connect(...) } ]);

  # Subref + DBIC-specific connection options
  ->connect_info(
    [
      sub { DBI->connect(...) },
      {
          quote_char => q{`},
          name_sep => q{@},
          on_connect_do => ['SET search_path TO myschema,otherschema,public'],
          disable_sth_caching => 1,
      },
    ]
  );

storageを設定するところのコードを後で読んでおけば、これで大体いけるんじゃないかなぁ。TODOリストに積む。

飲み会まとめ

06:12 |  飲み会まとめ - dann@catalyst を含むブックマーク はてなブックマーク -  飲み会まとめ - dann@catalyst  飲み会まとめ - dann@catalyst のブックマークコメント

始発で帰ったから、眠すぎて記憶が飛んでる... けど、楽しかったなぁ。こういうContextが共有された状況で限定された話を密にできるといいなと。

昼の部は、masakiさんとvkgtaroさんとCatalystでものづくり。Tipsも何点か教えてもらって、足りない部分とかがわかったのはよかったなぁ。こういうコード見ながらやるのは一番いいなと。

夜の部は、ZIGORuさんとlyokatoさんと殆ど話をしてた気がするなぁ。typesterさんとcharsbarさんとはあまり話せなかったから、次回CatalystConで。

  • REST APIapi/hoge.jsonのような形に対応するためにリクエストでの自動デシリアライズはやってないとか
    • ここはちょっと理解が足りないかも。けど、Catalyst::Action::RESTだとできない。
  • conten-typeによるViewの自動切り替え
  • FILTERSにフィルタを追加する
    • Template::Filters::LazyLoaderを読むといいよ
    • Template-ToolkitのPluginとか作ってUseしてたけど、Useだるーと思ってたけど、こんな方法で綺麗に追加できるのかと思った。
  • Debug環境でStatic::Simpleとかを読み込まないようにする
    • こういうのは作っとかなきゃなと思った。Starterにいれておくべきものだなと。
  • zigorouさんがServiceクラスのメソッドでTransactionの開始終了を行うモジュールを作っているとか!
    • ServiceのProxyクラスでServiceクラスにディスパッチしてるとか。Mooseじゃなくて、そういうほうほうでやるのもいいかもな。AUTOLOAD使ってディスパッチするんでもいいんだなと思った。
  • Validationをする場所
    • Controllerのアクション単位でValidationするとうまく共通化できずに、一箇所変更が入ると他の箇所に影響が波及しやすい。けど、ModelでやるのもFormとModelが1:1に対応してないとValidationをModelでやるのも難しいと。
  • Catalyst::Plugin::ConfigLoader使ってないとか。自前でCatalystに依存しないYAMLのLoader作ってる。
  • Active::RecordのMigrationは、みんなほしいと思ってる。

CatalystのInternalな話はある程度わかったけど、DBICのInternalな話は全然コード読んでないからさっぱりわからんかった。これは後でコード読まないとなぁ。

昼の部、夜の部参加の皆様お疲れ様でしたー!CatalystConも楽しみにしてま~す

# 今日の教訓は、電話番号は聞いておくことw

vkgtarovkgtaro2008/03/30 22:05おつでしたー。
そしてお邪魔しましたー。
奥様にもよろしくお伝えください。

danndann2008/03/30 23:57
おつでしたー。また遊びにきてくださーい。では。

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080329

2008-03-28

MooseでAOP

02:46 |  MooseでAOP - dann@catalyst を含むブックマーク はてなブックマーク -  MooseでAOP - dann@catalyst  MooseでAOP - dann@catalyst のブックマークコメント

Moose使えば、AOPに近いことができそう。before, afterがある。クラスごとにbefore, after書いていかないといけないみたいだから、そこがいわゆるAOPとは違って、DRYではないし、スマートではないけれど。それでも十分使えそう。

cachingとtransaction周りのコードで使えるんじゃないかなぁ。明日試す。けど、誰もMoose使ってないね...

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080328

2008-03-26

catsetup.pl - Catalystアプリ生成用スターター

01:04 |  catsetup.pl - Catalystアプリ生成用スターター - dann@catalyst を含むブックマーク はてなブックマーク -  catsetup.pl - Catalystアプリ生成用スターター - dann@catalyst  catsetup.pl - Catalystアプリ生成用スターター - dann@catalyst のブックマークコメント

フレームワーク用のStarterでは、テンプレート群やCSSJavaScriptのライブラリや、各種のベースクラスなどファイル群など、生成したりそのままコピーしたいファイル群が多いので、pmsetupなどのように1ファイルだけで管理するより、各種ファイルはディレクトリ構成のあるSkeltonディレクトリ中にテンプレートという形で置き、そのテンプレート中の変数や変数化されたディレクトリ名を置換するという形で、アプリケーションの雛形を生成するほうがよいのではないかと思っていました。

というような先週tomyhero邸でしていたところ、tomyheroさんがOreOre starter(http://svn.coderepos.org/share/lang/perl/OreOre-Starter/trunk/lib/OreOre/Starter.pm)を作ってくれたので、全力でパクってみました。

ロジックは再構成して、1ファイルにまとめました。また、starterでは設定ファイルを必要としない形のほうがいいかなというのと、汎用性を捨ててCatalyst専用Starterにしたほうが使いやすいかなということで、汎用性を捨ててConfigurationいらずで使えるようにしています。

後でCodeReposCatalystアプリのSkeltonと一緒にUpします。

catsetup.pl

#!/usr/bin/env perl

use strict;
use warnings;

use File::Spec;
use File::Path;
use Path::Class;
use IO::All;
use Getopt::Long;
use Pod::Usage;

GetOptions( \my %opt, qw/help/, 'skel=s', 'skel_path=s' );
pod2usage(2) if $opt{help};

check_dependencies();

my $app_name       = shift @ARGV;      # MyApp
my $lower_app_name = lc($app_name);    # myapp
my $upper_app_name = $app_name;        # MYAPP

my @exclude_rules = qw/\.svn \.cvs/;

my $app_dir = $app_name;

my $skelton_dir = skelton_dir();

pod2usage(2) unless $skelton_dir;

my $variables = {
    app_name       => $app_name,
    lower_app_name => $lower_app_name,
    upper_app_name => $upper_app_name,
};

my @all_files = ();
my @all_dirs  = ();
mk_app();

sub skelton_dir {
    if (   !$opt{skel}
        && !$opt{skel_path}
        && -d "$ENV{HOME}/.catsetup/skelton/default" )
    {
        return "$ENV{HOME}/.catsetup/skelton/default";
    }

    if ( $opt{skel_path} ) {
        return $opt{skel_path};
    }

    if ( $opt{skel} && -d "$ENV{HOME}/.catsetup/skelton/$opt{skel}" ) {
        return "$ENV{HOME}/.catsetup/skelton/$opt{skel}";
    }
    return;
}

sub check_dependencies {

    #basic func
    require Catalyst::Plugin::BuildURI;
    require Catalyst::Plugin::Static::Simple;
    require Catalyst::Plugin::RequestToken;

    # Encoding and I18N
    require Catalyst::Plugin::FillInForm::ForceUTF8;
    require Catalyst::View::TT::ForceUTF8;
    require Catalyst::Plugin::I18N;
    require Catalyst::Plugin::Unicode;

    # Authentication
    require Catalyst::Plugin::Authentication;
    require Catalyst::Plugin::Session;
    require Catalyst::Plugin::Session::Store::FastMmap;
    require Catalyst::Plugin::Session::State::Cookie;

    # Validation
    require Catalyst::Plugin::FormValidator::Simple;
    require Catalyst::Plugin::FormValidator::Simple::Auto;

    # Email
    require Catalyst::Plugin::Email::Japanese;

    # API
    require Catalyst::Action::REST;
    require Catalyst::Controller::Resources;
    require Catalyst::View::JSON;
}

sub mk_app {
    mk_app_dir();
    collect_skelton_dir_and_files();
    mk_module_dirs( \@all_dirs );
    render_templates( \@all_files );
    make_scripts_executable();
}

sub mk_app_dir {
    mk_dir($app_dir);
}

sub mk_module_dirs {
    my $dirs = shift;
    for my $dir (@$dirs) {
        my $to_dir = to_file_path($dir);
        mkpath($to_dir);
    }
}

sub collect_skelton_dir_and_files {
    dir($skelton_dir)->recurse(
        callback => sub {
            my $file = shift;

            for my $exclude_rule (@exclude_rules) {
                if ( $file =~ /$exclude_rule/ ) {
                    return;
                }
            }

            if ( -f $file ) {
                $file =~ s/$skelton_dir//;
                $file =~ s/^\///;
                push @all_files, $file;
            }

            if ( -d $file ) {
                $file =~ s/$skelton_dir//;
                $file =~ s/^\///;
                push @all_dirs, $file;
            }
        }
    );

}

sub render_templates {
    my $templates = shift;
    my $tt        = Template->new(
        { INCLUDE_PATH => $skelton_dir, TAG_STYLE => 'star' } );
    for my $t (@$templates) {
        my $to_file = to_file_path($t);
        my $output;
        $tt->process( $t, $variables, \$output )
            || die $tt->error();
        $output > io($to_file);
        print qq/generated "$to_file"\n/;
    }
}

sub to_file_path {
    my $original_file_path = shift;
    foreach my $key ( keys %{$variables} ) {
        $original_file_path =~ s/__$key\__/$variables->{$key}/g;
    }
    return File::Spec->catfile( $app_dir, $original_file_path );
}

sub mk_dir {
    my ($dir) = @_;
    if ( -d $dir ) {
        print qq/ exists "$dir"\n/;
        return 0;
    }
    if ( mkpath $dir ) {
        print qq/created "$dir"\n/;
        return 1;
    }

    die "Couldn't create $dir";
}

sub make_scripts_executable {
    !system "chmod 755 ./$app_dir/script/*" or die $?;
}


=head1 SYNOPSIS

catsetup.pl MyApp

Options:
  --skel Catalystアプリのskelton
        ~/.catsetup/skelton/<skel>
  --skel_path Catalystアプリのskeltonのディレクトリ

  何もオプションが指定されない場合は、
  ~/.catsetup/skelton/default
  のskeltonが選択される。

=cut

=head1 DESCRIPTION

Catalstアプリの雛形から、Catalystアプリを生成するStarter

# 誰かがこれをさらにパクって修正しちゃってくれたりするといいなぁ。

# 週末の昼の部は何を作るかなぁ。週末はこれ作ろうかと思っていたんだけど。

TODO

  • パスの扱い方がいまいち
    • 収集するディレクトリとファイルのパスはPath::Classのオブジェクトとして扱う
  • 変数化されたパスのreplaceはPath::Classにメソッドはやしてやってしまいたい
  • ディレクトリとファイルのパス群がグローバル変数になっているのを修正したい
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080326

2008-03-25

dannさんを囲む会で、何故か自分が店の予約をしている件について

01:30 |  dannさんを囲む会で、何故か自分が店の予約をしている件について - dann@catalyst を含むブックマーク はてなブックマーク -  dannさんを囲む会で、何故か自分が店の予約をしている件について - dann@catalyst  dannさんを囲む会で、何故か自分が店の予約をしている件について - dann@catalyst のブックマークコメント

tomyheroのひどい丸投げをみた!

ということで、他に参加される方がいれば、techmemo@gmail.com or この日記のコメントにでも参加希望の旨連絡ください。木曜日までに連絡もらえれば予約しておきます。

参加確定リストの中に知り合いがいない人は、techmemo@gmail.comまで連絡してもらえれば、僕の連絡先をメールでお伝えしますので、e-mailで。

参加者リスト

確定

未確定

未確定の方については予約はせずに、当日追加という形にしようかと思っています。もしくはtomyheroの膝の上で。

日時・場所

日時: 3/29(土)

集合場所: 渋谷ハチ公前 19:00

飲み屋: 4番サード魚真

http://r.gnavi.co.jp/g193102/

YAPC::Asiaのチケット購入 !!!

20:57 |  YAPC::Asiaのチケット購入 !!! - dann@catalyst を含むブックマーク はてなブックマーク -  YAPC::Asiaのチケット購入 !!! - dann@catalyst  YAPC::Asiaのチケット購入 !!! - dann@catalyst のブックマークコメント

行けるか微妙ではあるけど頑張っていきたい! それにしてもYAPC::Asiaは豪華だなぁ。

ikasam_aikasam_a2008/03/26 10:51確定でお願いします.囲みに行きます.

vkgtarovkgtaro2008/03/26 14:41ikasam_a さんktkr!

danndann2008/03/26 23:54当日楽しみにしてます:)

ZIGOROuZIGOROu2008/03/27 00:50これ僕も参加しても大丈夫ですかねー

ikasam_aikasam_a2008/03/27 02:27>id:ZIGOROu
いいんじゃないんでしょうか :)

danndann2008/03/27 02:37ZIGOROuさん、Welcomeです!

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080325

2008-03-24

今週末tomyheroさんが囲ってくれるんですが、いつのまにかプチCatalystConになりつつある件について

22:08 |  今週末tomyheroさんが囲ってくれるんですが、いつのまにかプチCatalystConになりつつある件について - dann@catalyst を含むブックマーク はてなブックマーク -  今週末tomyheroさんが囲ってくれるんですが、いつのまにかプチCatalystConになりつつある件について - dann@catalyst  今週末tomyheroさんが囲ってくれるんですが、いつのまにかプチCatalystConになりつつある件について - dann@catalyst のブックマークコメント

id:tomyheroさん、id:hidedenさん、id:vkgtaroさん、id:a666666さんといったlazy-peopleな人たちと、id:typesterさん、id:lyokatoさん、id:ikasam_aさんといったCatalystな人たちといった、えらく豪華なメンバーが集まってます。

最初はlazy-peopleの飲み会だったはずなんですが、いつの間にかプチCatalystConですね。lazy-peopleな人たちをはじめ、Perl界隈で活躍されてる人に会えるのはとても楽しみです。これも一重にlazy-peopleな人のおかげです。

lazy-people++

# けど、誰も店を予約してないけどどっか店に入れるんだろうかw 想定外の人数になっている気がするよ!

# tomyheroさんから店予約しといて!って言われてる>_<。囲われる人なのに、店の予約しちゃってるんですけど!tomyhero--

TDD神話

22:08 |  TDD神話 - dann@catalyst を含むブックマーク はてなブックマーク -  TDD神話 - dann@catalyst  TDD神話 - dann@catalyst のブックマークコメント

自分は常々、テスト駆動開発とサービス開発は相性が悪いなと思っていました。新しい機能を作っているときや、新しいサービスを作っているときは、自分でも答えが見えていない状態で作っていることが多くあります。コードを書いているうちに少しずつ問題が解決されていって、最初は見えていなかったものが見えるようになり、答えがみえるようになる、ということが多々あります。作っては壊しを何度も繰り返すこともあります。

http://d.hatena.ne.jp/naoya/20080324/1206354054

naoyaさんのTDDの話はリアリティがありますね。僕も2003-2005にかけてTDDとテスタビリティについて真剣に取り組んだ時期があって、当時以下のような記事を書いていました。

http://d.hatena.ne.jp/dann/20050823

当時はとあるプロダクト開発で痛い目にあっていたので、真剣にテスタビリティについて考えていました。以下の3冊は、そんなときに読んだ3冊で、テスタビリティを重視した設計を行う方法を考えさせてくれたとても貴重な本だったと思います。以下の本で勉強したことは今も役に立っています。

  • Agile Software Development - Principles, Patterns and Practices
  • Working Effectively With Legacy Code
  • Test Driven Development

最近は、テストのためのパターン本の集大成の本もでていて、テスタビリティを考慮した設計を考えるうえでのボキャブラリもそろってきた感じがします。

そんなこんなで、Javaで培ったノウハウをどうやったらPerlでも活かせるかを考えながら、Catalystを触っている今日この頃です。

トランザクション境界の設定について

20:33 |  トランザクション境界の設定について - dann@catalyst を含むブックマーク はてなブックマーク -  トランザクション境界の設定について - dann@catalyst  トランザクション境界の設定について - dann@catalyst のブックマークコメント

トランザクション境界はサービス層のインタフェースで設定されるべきものなんだろうなと思っています。Java界隈ではこのようなトランザクション境界になるインターフェースをもつクラスをサービスと呼ぶことが多いです。

Serviceのメソッド内でトランザクションの開始や終了のコードを記述してしまう場合、そのServiceクラスのロジックがDBに依存してしまうため、Testabilityを考えると望ましくありません。DBなしではテストがしにくくなるからです。Java界隈ではAOP + DIコンテナでこの問題を解決しています。具体的には、AOPのインターセプタでサービスクラスのメソッドの前後でトランザクションの開始・終了をできるようにするということをやっています。このようにすることで、サービスクラスのロジックには、トランザクションの開始・終了のロジックが入らないため、そのクラスはトランザクションマネージャには依存せず、単体でテストができるようになります。

DBIx::Classを使う場合、どのようにしたらよいのかを少し考えてみました。

DBIx::Classでは、Schemaのtxn_doのメソッドを使って、Unit of workの実行をするのが推奨されているようです。このメソッドは、実行したいUnit of workを、トランザクションの開始と終了で挟んでくれるラッパーメソッドなのですが、これを使うということは、Serviceのロジック中にトランザクションの開始・終了のコードが埋まってしまうことになります。

これだとテストがしにくくなるので、ServiceのロジックをDBに依存せずに単体でテストをするには、どのような方法があるかを考えてみました。

コードをみた限りでは、

Schemaのtxn_doをオーバーライドして、トランザクションの開始、終了を行わないコードにオーバーライドする」

というのが一番簡単でLLらしい解なのではないかと思いました。

これだったら簡単にできますし、割と現実的な気がします。Perl界隈でAOPが流行らないのも、こうしたmonkey patchingで回避できてしまうからというのもあるのかなという気もします。

# LogicパッケージはServiceパッケージに名前を変えることにしました(名前だけ変えただけなんですが)

connectionの永続化

20:54 |  connectionの永続化 - dann@catalyst を含むブックマーク はてなブックマーク -  connectionの永続化 - dann@catalyst  connectionの永続化 - dann@catalyst のブックマークコメント

http://d.hatena.ne.jp/naoya/20060912/1158058322

「各ウェブサーバーのプロセスごとにデータベースへの接続をメモリ内で保持して解放しないでおいて、次のリクエストでもそれを使いまわす」

というものらしい。

  • メリット
    • 1プロセスの中での接続は共有できて節約できる
  • デメリット
    • 複数台のアプリケーションサーバーを並べたときに、プロセスが立ち上がるため、同時接続数がWebサーバーのプロセス数と同一になるため、DB側のリソースを消費する。

だから、connectionを永続化するのはスケールさせるためには、DB側のリソースを消費しすぎるために良くないという話のようです。

MySQLでは毎回コネクションを開いて閉じても、それが遅さの原因となるほど支配的にならないので、1リクエスト内でハンドラを共有すればよい。」

ということなので、1リクエスト内でdbhを共有してもいいんじゃないかということです。こういうのは確かにやってみないとわからない情報でとても貴重ですね。

# この前書いたモデルクラスは、Schema取得する度にconnectしちゃうので、あそこをどうするのがいいのかなと思っていたのでした。

リクエスト毎にconnectする方法について

20:54 |  リクエスト毎にconnectする方法について - dann@catalyst を含むブックマーク はてなブックマーク -  リクエスト毎にconnectする方法について - dann@catalyst  リクエスト毎にconnectする方法について - dann@catalyst のブックマークコメント

DBIx::Classの場合、リクエスト毎にconnectして、そのSchemaを保持しておけばいいということでいいのかな。

リクエスト毎にconnectして、そのスキーマを保持しておくのは、CatalystのPluginを作って、prepareメソッドでconnectすればよさそう。SchemaFactoryを用意しておいて、PluginのprepareメソッドでSchemaのオブジェクトを保持させる。Schemaオブジェクトを参照する側では常にSchemaFactoryを参照して、Schemaを取得するようにするという形にしとけばいいのかなぁ。

mod_perl下で動かす場合に、生成したSchemaインスタンスはどこで破棄すればいいのかな。破棄しないで、リクエストを処理する度にSchemaクラスでconnectして、Schemaのオブジェクトを上書きする形にすればいいのかな。

mod_perlDBICについて、少し知識が足りないので、ここら辺はちょっと勉強しないといけないな。Catalystは何とかなりそうな気がしてきたから、mod_perlDBICについて誰かえろい人に詳しく解説してもらいたい!mod_perlDBICMLの過去ログを一通り少しみてみるかなぁ。

しかし、あまりまとまった情報というのがどこにも書いてないなぁ。誰か知ってたら教えてください!

ChiekoChieko2012/11/01 15:31I was struck by the honesty of your potisng

aiyuyrgcaiyuyrgc2012/11/02 09:387BKNqu <a href="http://ijiiwgkjkaif.com/">ijiiwgkjkaif</a>

ilriywkxakilriywkxak2012/11/02 14:161SPQ1u , [url=http://zmmsipceuptq.com/]zmmsipceuptq[/url], [link=http://tusdfnnozdtx.com/]tusdfnnozdtx[/link], http://opfewxqzzglm.com/

zurniozurnio2012/11/04 22:5754iu8a <a href="http://zxsyrcqrfrng.com/">zxsyrcqrfrng</a>

rohkpwrohkpw2012/11/05 12:17OwmVsT , [url=http://ckwpxlidpoej.com/]ckwpxlidpoej[/url], [link=http://wgbfbocmmjow.com/]wgbfbocmmjow[/link], http://pwztavcxwtnm.com/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080324

2008-03-23

認証周りのベースクラス

00:09 |  認証周りのベースクラス - dann@catalyst を含むブックマーク はてなブックマーク -  認証周りのベースクラス - dann@catalyst  認証周りのベースクラス - dann@catalyst のブックマークコメント

を今日は作っていた。

Authentication::Store::DBIx::Class::Userは$c->modelを参照しているので、Catalystのモデルに依存しないようにするために、このクラスをオーバーライドして、Userのresultsetを直接取得するようにした。

ただ、Authentication::Store::DBIx::Class::Userのようなクラスのメソッドをオーバーライドするのはよくないかもしれない。UserだけCatalystのモデルにするのでもいいかもしれないと少し思う今日この頃。

catstarterからOreOre Starterへの移行方法

23:42 |  catstarterからOreOre Starterへの移行方法 - dann@catalyst を含むブックマーク はてなブックマーク -  catstarterからOreOre Starterへの移行方法 - dann@catalyst  catstarterからOreOre Starterへの移行方法 - dann@catalyst のブックマークコメント

catstarterで作ったものも、OreOre Starterに移行させたよいよね

ってことで移行してみた。

catstarterでの生成

catstarter.pl MyApp -new

変数のテンプレート化

find . -type f| xargs sed -i 's/myapp/[* appprefix *]/g'
find . -type f| xargs sed -i 's/MYAPP/[* upper_appprefix *]/g'
find . -type f| xargs sed -i 's/MyApp/[% module %]/g'

ディレクトリ名、スクリプト名の変数化

  • __module__ のようにMyAppディレクトリを書き換える。
  • myapp_create.plは__appprefix___create.plのように書き換える

OreOre starterの使い方

23:42 |  OreOre starterの使い方 - dann@catalyst を含むブックマーク はてなブックマーク -  OreOre starterの使い方 - dann@catalyst  OreOre starterの使い方 - dann@catalyst のブックマークコメント

http://d.hatena.ne.jp/tomyhero/20080323

OreOre starterはTTのテンプレートから、YAMLで指定された変数で一括置換して、

アプリケーションの雛形を作るStarter。

ディレクトリを作成しておいて、そのディレクトリ構成にしたがってTTのテンプレートを置いておけば、TT中の変数が一括置換されて、アプリケーションのファイルが生成されます。

1つのファイルのstarterだけだと、ファイルの編集が大変ですが、このOreOre staterのようにディレクトリ構成を作れて、そこにテンプレートを置いて置けるようにしておけば、ファイルの編集が簡単になります。また、CSSJSなどのライブラリ群を置いておくこともできます。

まさに、OreOre Starterです。

こんな感じの作りたいねといったら、tomyheroさんが作ってくれました! tomyhero++

作成する手順は以下の通りです。

  • configの作成
  • Skelton用のディレクトリの作成とテンプレートの配備
  • starterの実行

以下では、ステップ順に説明していきます。

configの作成

以下のような感じ

fromに指定されたSkeltonのディレクトリから、

カレントディレクトリ以下にnameの変数名で

指定されたアプリケーションが作成される。

replaceでは、TTの変数名が指定できる

starは、TTのtag style。

name    : MyApp
from    : /home/dann/workdir/CatSkel
tag_style : star
ignore  :
    - \.svn
    - \.cvs
replace :
    module : MyApp
    appprefix: myapp
    upper_appprefix: MYAPP

ディレクトリ構成やテンプレート化

catalyst.plやcatstarterなどで作ればいいです。

  • ディレクトリやファイル名は、 __module__ というように__variable__と指定して変数化する

例えば、

  • MyAppというディレクトリは、__module__というように書き換える
  • myapp_create.plは__appprefix___create.plのように書き換える

oreore starterの実行

./bin/oreore-starter.pl -c conf/config.yaml

tomyherotomyhero2008/03/24 00:51sample/ 配下に

oreore-starter-catalyst.pl 置いてみたよ!

danndann2008/03/25 00:03thanks !!!

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080323

2008-03-22

TODO

02:11 |  TODO - dann@catalyst を含むブックマーク はてなブックマーク -  TODO - dann@catalyst  TODO - dann@catalyst のブックマークコメント

  • 認証周りのベースコントローラ
  • FormValidation周りのシンプル化
  • remember me
  • Feed部分のロジックの共通化
  • Catalystに依存しないモデル部分の改善
  • DBICのMock化

くらいかなぁ。ここまでができると、Catalystで基本的な仕組みは勉強できたような気がするなぁ。そこまで終わったらなんか作っていこうかなぁと。

Catalystに依存しないモデルのロジックについて

00:22 |  Catalystに依存しないモデルのロジックについて - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないモデルのロジックについて - dann@catalyst  Catalystに依存しないモデルのロジックについて - dann@catalyst のブックマークコメント

  • DBのみに依存したロジックは、全部Schemaにかいていく。MyApp::Base::Model::DBICを継承したクラスは自動生成して、Logicはそこには記述しない。
  • Logicクラスには、複数のModelクラス(Catalyst非依存)を参照するようにする。

という形にしようかなぁと。

まだ、「MyApp::Base::Model::DBICを継承したクラスの自動生成」ができてないけど、ここはschema_dumperに手をいれてやろうかと思ったたり。別のスクリプトでもいいかもしれないけど。

なんで、MyApp::Base::Model::DBICを継承したモデルクラスにロジックを記述しないかっていうと、スクリプトで自動生成したいから。それ以外の理由はなかったりする。

Catalystに依存しないDBICのモデルクラス

00:04 |  Catalystに依存しないDBICのモデルクラス - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないDBICのモデルクラス - dann@catalyst  Catalystに依存しないDBICのモデルクラス - dann@catalyst のブックマークコメント

を作ってみた。

Catalystに依存しないでDBICを使うにはどうすればいいんだろうとこの連休は考えていた。Schemaってやつが情報を知りすぎていて、どうもクラスに分割しにくい。全部のテーブル情報知っちゃってるからで、どうしたもんかなぁと思っていた。

resultsetごとにクラスが分割できるようにベースクラスを作ればいいんだと思った。ベースクラスでやってるのはSchema取得。resultsetを取得して、そのresultsetに対して処理ができるようにするということ。

モデルのクラスからは、SchemaやResultsetが直接みえないようになる。けど、DBICのメソッドは直接触りたいから、AUTOLOADで処理をディスパッチする。という感じ。

Logicのクラスで$schema->resultset('Feeds')->search() とかはしたくないなぁと思って、綺麗に扱えないかっていうだけのことなんだけれど。多くの処理は、MangoのProviderからのパクリですが... ただ、Mangoはメソッドをラップして、Domainモデルを作ろうとしているんだけれど、これは自分には必要ないので、AUTOLOADでresultsetに全部ディスパッチするようにしたというのが違いかな。

モデルのベースクラス(今のところ特に何もしてない)

package MyApp::Base::Model;
use strict;
use warnings;

BEGIN {
    use base qw/Class::Accessor::Grouped/;
};

sub new {
    my ($class, $args) = @_;
    my $self = bless {}, $class;

    $self->setup($args);

    return $self;
};

sub setup {
    my ($self, $args) = @_;

    if (ref $args eq 'HASH') {
        map {
            if ($self->can($_)) {
                $self->$_($args->{$_})
            } else {
                $self->{$_}  = $args->{$_};
            }
        } keys %{$args};
    };

    return;
};

1;

Catalystに依存しないDBIC用のモデルのベースクラス

package MyApp::Base::Model::DBIC;
use MyApp::ConfigLoader;
use strict;
use warnings;
BEGIN {
    use base qw/MyApp::Base::Model/;
    use UNIVERSAL;
    use MyApp::Exception qw/:try/;

    __PACKAGE__->mk_group_accessors('component_class', qw/schema_class/);
    __PACKAGE__->mk_group_accessors('inherited', qw/
        config
        source_name
        connection_info
        _resultset
        _schema
    /);
};
__PACKAGE__->schema_class('MyApp::Schema');

sub new {
    my $class = shift;
    my $self  = $class->SUPER::new(@_);
    $self->setup_config;
    $self->setup_connection_info;
    return $self;
}

sub setup_config {
    # FIXME
    # Catalystで使うときは、configloaderがセットされた状態にする。
    my ($self) = @_;
    unless($self->config) {
        my $config = MyApp::ConfigLoader->new->config;
        $self->config($config);
    }
}

sub setup_connection_info {
    my ($self) = @_;
    $self->connection_info(
        $self->config->{'Model::DBIC'}{'connect_info'}
    );
}

sub resultset {
    my ($self, $resultset) = @_;

    if (defined $resultset) {
        $self->_resultset($resultset);
    } elsif (!$self->_resultset) {
        if (!$self->source_name) {
            MyApp::Exception->throw('SCHEMA_SOURCE_NOT_SPECIFIED');
        };

        try {
            $self->_resultset($self->schema->resultset($self->source_name));
        } except {
            MyApp::Exception->throw('SCHEMA_SOURCE_NOT_FOUND', $self->source_name);
        };
    };

    return $self->_resultset;
};

sub schema {
    my ($self, $schema) = @_;

    if ($schema) {
        $self->_schema($schema);
    } elsif (!$self->_schema) {
        if (!$self->schema_class) {
            MyApp::Exception->throw('SCHEMA_CLASS_NOT_SPECIFIED');
        };
        $self->_schema(
            $self->schema_class->connect(@{$self->connection_info || []})
        );
    };

    return $self->_schema;
};

sub DESTROY {}

sub AUTOLOAD {
    my $self = shift;
    my $method = our $AUTOLOAD;
    $method =~ s/.*:://o;
    warn $method;
    $self->resultset->$method(@_);
}

1;

実際のモデルのクラス

package MyApp::Model::Feeds;
use base qw/MyApp::Base::Model::DBIC/;

__PACKAGE__->source_name('Feeds');

1;

使用方法

    my $feed = MyApp::Model::Feeds->new;
    $feed->update_or_create(
        {   feedlink => 'http://hoge.com/',
            link     => 'hoge.com',
            title    => 'hogehoge'
        }
    );

# まじめにここの仕組みは考えたいから、これじゃ使えないよ!とか突っ込み希望です。

改善案

AUTOLOADを1回だけ呼ぶようにする

http://d.hatena.ne.jp/ysano2005/20051229/1135870740

vkgtaro++

全部Resultsetにdispatchするんだったら、newする段階でresultset返しちゃえばいいんじゃないかというtomyheroさんからの意見。ごもっとも!tModelクラスにロジックを書くという案を最初考えていたから、Resultsetにディスパッチしてるんだけど、実はそれだと自動生成が面倒になるので、それもいいかもしれない!

Catalystのディレクトリ構成(続)

21:12 |  Catalystのディレクトリ構成(続) - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystのディレクトリ構成(続) - dann@catalyst  Catalystのディレクトリ構成(続) - dann@catalyst のブックマークコメント

catstarterで生成して、script/myapp_web_schema_dumper.plを実行した後の状態。MyApp::Web::Modelは使わないというところまで、後少し。今は以下のような感じ。後一歩。

  • MyApp::Web::Controller配下にベースクラスを置かないようにBase用のディレクトリを分離したこと。
  • MyApp::Web::Modelは使わずに、MyApp::ModelというCatalystに依存しないようにするためのModel用のパッケージを用意したところ
`-- MyApp
    |-- Base
    |   |-- Model
    |   |   `-- DBIC.pm
    |   |-- Model.pm
    |   `-- Web
    |       |-- Controller
    |       |   `-- API.pm
    |       |-- Model
    |       `-- View
    |-- CLI
    |   |-- Command
    |   `-- Command.pm
    |-- CLI.pm
    |-- ConfigLoader.pm
    |-- Date.pm
    |-- Exception.pm
    |-- I18N
    |-- Logic
    |-- Model
    |-- Schema
    |   |-- CrawlStatuses.pm
    |   |-- Favicons.pm
    |   |-- Feeds.pm
    |   |-- Folders.pm
    |   |-- Items.pm
    |   |-- Members.pm
    |   |-- Pins.pm
    |   |-- SchemaInfo.pm
    |   `-- Subscriptions.pm
    |-- Schema.pm
    |-- Utils.pm
    |-- Web
    |   |-- Controller
    |   |   |-- API
    |   |   |-- API.pm
    |   |   `-- Root.pm
    |   |-- Model
    |   |   `-- DBIC.pm
    |   `-- View
    |       |-- JSON.pm
    |       |-- TT
    |       |   `-- Plugin
    |       `-- TT.pm
    `-- Web.pm
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080322

2008-03-21

CatalystのBaseのController

01:22 |  CatalystのBaseのController - dann@catalyst を含むブックマーク はてなブックマーク -  CatalystのBaseのController - dann@catalyst  CatalystのBaseのController - dann@catalyst のブックマークコメント

Namespaces

When you try to put a base class for a Model, View or Controller directly under your MyApp directory as, for example, MyApp::Controller::Foo, you will have the problem that Catalyst will try to load that base class as a component of your application. The solution is simple: Use another namespace. Common ones are MyApp::Base::Controller::* or MyApp::ControllerBase::* as examples.

Controllerのベースクラスは、Controller以下においちゃうとコンポーネントとしてロードしちゃうから違うところにおいてねと。ということで、MyApp::Base::Controller配下におくようにした。ただ、なんか綺麗じゃないなぁ。

monit入門

20:39 |  monit入門 - dann@catalyst を含むブックマーク はてなブックマーク -  monit入門 - dann@catalyst  monit入門 - dann@catalyst のブックマークコメント

Catalystアプリのmonitoring用に使ってみた。apache2とmysqlが落ちてたら自動で再起動が基本的な動作。configがDSLっぽくかけるのは気持ちがいいですね。再起動用途に使うのは案外いいかも。

設定

/etc/monit/monitrc

###############################################################################
## Global section
###############################################################################
##
## Start monit in background (run as daemon) and check the services at 10 second
## intervals.

set daemon 10

#
## Set syslog logging with the 'daemon' facility. If the FACILITY option is
## omited, monit will use 'user' facility by default. You can specify the
## path to the file for monit native logging.

set logfile syslog facility log_daemon


## Monit has an embedded webserver, which can be used to view the
## configuration, actual services parameters or manage the services using the
## web interface.
#
set httpd port 2812 and
     use address 192.168.0.30  # only accept connection from 192.168.0.30
     allow localhost        # allow localhost to connect to the server
     allow 192.168.0.0/255.255.255.0
     allow admin:monit      # require user 'admin' with password 'monit'

###############################################################################
## Includes
###############################################################################
##
## It is possible to include the configuration or its parts from other files or
## directories.

include /etc/monit/conf.d/*

/etc/monit/conf.d/apache.monitrc

# Monitoring the apache2 web services.
# It will check process apache2 with given pid file.
# If process name or pidfile path is wrong then monit will
# give the error of failed. tough apache2 is running.
check process apache2 with pidfile /var/run/apache2.pid
    group apache
    start program = "/etc/init.d/apache2 start"
    stop program = "/etc/init.d/apache2 stop"
    if failed host 127.0.0.1 port 80
        protocol http request /index.html
    then restart
    if cpu > 95% for 5 cycles then restart
    if totalmem > 800.0 MB for 5 cycles then restart
    if children > 250 then restart
    if 3 restarts within 5 cycles then timeout

/etc/monit/conf.d/mysql.monitrc

#Monitoring Mysql Service

check process mysql with pidfile /var/run/mysqld/mysqld.pid
    group database
    start program = "/etc/init.d/mysql start"
    stop program = "/etc/init.d/mysql stop"
    if failed host 127.0.0.1 port 3306 then restart
    if 5 restarts within 5 cycles then timeout

/etc/monit/conf.d/ssh.monitrc

check process sshd with pidfile /var/run/sshd.pid
    start program "/etc/init.d/ssh start"
    stop program "/etc/init.d/ssh stop"
    if failed port 22 protocol ssh then restart
    if 5 restarts within 5 cycles then timeout

syntax check

% sudo monit -t

サービスの有効化

% sudo vi

/etc/default/monit

startup=1

管理画面

http://localhost:2812/ で管理画面

管理画面は自宅サーバー用にはいいかもね。台数が多いと複数台のサーバーを監視できるツールをいれたほうがよいような。

動作確認

tail -f /var/log/syslog

で監視しつつ、/etc/init.d/apache2 stop とかをやってみる。

TODO

  • set daemon 10が効いていない・・・。
    • デフォルト値の120秒で再起動するようになる。うーむ... 自宅サーバー用にはこれでもいいけど...
    • monit version は 4.8.1

APIの入出力フォーマット切り替え

13:51 |  APIの入出力フォーマット切り替え - dann@catalyst を含むブックマーク はてなブックマーク -  APIの入出力フォーマット切り替え - dann@catalyst  APIの入出力フォーマット切り替え - dann@catalyst のブックマークコメント

masakiさんのコメントが!

content typeでAPIの入出力のフォーマットを切り替え>自分はattributeベースでJavaのannotationみたくやってる

まだattributeでのイメージができてないので、今度詳しく教えてもらいたい!

その前にAttributeでやるのとActionでやるのと、どこら辺に違いがでてきそうなのかがわかってないので、Catalystのコードをちゃんと読まないとなぁ。

NicoNico2012/10/31 14:14Furrealz? That's marvleusoly good to know.

tgxbwphztgxbwphz2012/11/02 07:062v4LVy , [url=http://ztmlvjpwucfz.com/]ztmlvjpwucfz[/url], [link=http://qsgfbwuiewbv.com/]qsgfbwuiewbv[/link], http://kswsunemjnvs.com/

shfskejiuqshfskejiuq2012/11/02 23:53AxWUcP <a href="http://vbcntgoaeglm.com/">vbcntgoaeglm</a>

pbasztumsjypbasztumsjy2012/11/03 11:41XP122A , [url=http://hknpcgebdmvq.com/]hknpcgebdmvq[/url], [link=http://tnnoeusdepgf.com/]tnnoeusdepgf[/link], http://tjqusbwcnsli.com/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080321

2008-03-20

Mango::Catalyst::View::Feed

| 03:05 |  Mango::Catalyst::View::Feed - dann@catalyst を含むブックマーク はてなブックマーク -  Mango::Catalyst::View::Feed - dann@catalyst  Mango::Catalyst::View::Feed - dann@catalyst のブックマークコメント

http://search.cpan.org/src/CLACO/Mango-0.01000_10/lib/Mango/Catalyst/View/Feed.pm

RSSの生成とかをよくControllerで書いていたんだけれど、毎回似たような処理を書いているので、共通化できないものかなぁと思っていた。Viewの部分で吸収する方法が、Mango::Catalyst::View::Feedには書いてあって、こうすれば殆どの処理をView側で吸収できるなぁと思った。

as_feed, as_feed_entryが一番のポイントかなぁと。

# API側からは、このViewにForwardする形に後で変える。

# それにしても、clacoさんのコードは綺麗だなぁ。どのコードを読んでも綺麗。

DBICでマスターとスレーブの振り分けをやるのはどこでやる?

| 02:48 |  DBICでマスターとスレーブの振り分けをやるのはどこでやる? - dann@catalyst を含むブックマーク はてなブックマーク -  DBICでマスターとスレーブの振り分けをやるのはどこでやる? - dann@catalyst  DBICでマスターとスレーブの振り分けをやるのはどこでやる? - dann@catalyst のブックマークコメント

どこのレイヤでやるのが美しいんだろうかと思っていた。Readをスレーブに、Writeをマスターに振り分けるのであれば、どうもStorage::DBIでやるのが綺麗なように見える。

CPANを探してみると、Storage::DBI::Replicationというモジュールがあった。ざっとコードをみた感じでは、WriteはMasterに、ReadはSlaveにということだけしかやってないみたい。

ただ、これだと、レプリケーションのタイムラグがあった場合、WRITE直後のREADは失敗するから現実的には使えないんじゃないかと思った。レプリケーションが同期ってのも現実的じゃないから、こういうのはどこでハンドリングするべきなんだろうなぁというのが疑問。

Masterが1台であれば、Storage::DBIレベルで頑張るのがいいんじゃないかなぁという気がするのだけれど、どうなんだろう。マスター分割が必要になると、Storage::DBIレベルでは吸収できなそうだから、結局アプリケーションレベルでMasterとSlaveの振り分けが必要になってくるのかも、と思うとそもそもDBのスケールを考えるようなレベルのアプリケーションを作る場合には、Storage::DBIのレイヤでやるっていうのもあんまよくないのかな。

この辺の情報は、あまり世間にはでていないような気がするなぁ。綺麗に解決する解というのはないものかなぁと。

# id:hidedenに色々と教えてもらって、*現実解*は大体どういうものかは分かって、実装のイメージも大体できたんだけれど。hideden++

RESTの素朴な疑問

02:36 |  RESTの素朴な疑問 - dann@catalyst を含むブックマーク はてなブックマーク -  RESTの素朴な疑問 - dann@catalyst  RESTの素朴な疑問 - dann@catalyst のブックマークコメント

GET、POST、PUT、DELETEに対応させると、エンドポイントが綺麗に扱えるのは確かに美しいんだけど、現実的にはGET, POSTだけで扱えるAPIがあったほうが便利なんじゃないかという素朴な疑問。AtomPubが普及していくと、みんなREST万歳!ってなるんだろうかなぁ。

CatalystのREST API用のベースクラス

| 02:23 |  CatalystのREST API用のベースクラス - dann@catalyst を含むブックマーク はてなブックマーク -  CatalystのREST API用のベースクラス - dann@catalyst  CatalystのREST API用のベースクラス - dann@catalyst のブックマークコメント

を作ってみた。

  • dispatch部分は、Catalyst::Controller::Resources
    • Catalyst::Controller::RESTは、dispatchする部分が綺麗にかけないので、Catalyst::Controller::Resourcesを使う
  • エンドポイントへのデータのデシリアライズとシリアライズはCatalyst::Action::SerializeとDeserializeを使う

という形にしてみた。

これで、Content-Typeによって、シリアライザ・デシリアライザが切り替わるので、APIで複数のフォーマットに簡単に対応できるんじゃないかなぁ。かつ、エンドポイントはRailsライクに綺麗に扱えると。

Catalyst::Controller::RESTを使ってみて、一通りやりたいことはできたのだけれどあまりにdispatchしたところのコードが汚くなるので、こりゃあかんということでいいとこどりをしてみた。

APIのベースクラス

package MyApp::Web::Controller::API;
use strict;
use warnings;
use Params::Validate qw(SCALAR OBJECT);
use base 'Catalyst::Controller::Resources';

__PACKAGE__->config(
    'stash_key' => 'entity',
    'default'   => 'text/x-json',
    'map'       => {
        'text/html'          => 'YAML::HTML',
        'text/xml'           => 'XML::Simple',
        'text/x-yaml'        => 'YAML',
        'text/x-json'        => 'JSON',
    }
);

sub begin : ActionClass('Deserialize') {
}

sub end : ActionClass('Serialize') {
}


=item status_ok

Returns a "200 OK" response.  Takes an "entity" to serialize.

Example:

  $self->status_ok(
    $c,
    entity => {
        radiohead => "Is a good band!",
    }
  );

=cut

sub status_ok {
    my $self = shift;
    my $c    = shift;
    my %p    = Params::Validate::validate( @_, { entity => 1, }, );

    $c->response->status(200);
    $self->_set_entity( $c, $p{'entity'} );
    return 1;
}

=item status_created

Returns a "201 CREATED" response.  Takes an "entity" to serialize,
and a "location" where the created object can be found.

Example:

  $self->status_created(
    $c,
    location => $c->req->uri->as_string,
    entity => {
        radiohead => "Is a good band!",
    }
  );

In the above example, we use the requested URI as our location.
This is probably what you want for most PUT requests.

=cut

sub status_created {
    my $self = shift;
    my $c    = shift;
    my %p    = Params::Validate::validate(
        @_,
        {
            location => { type     => SCALAR | OBJECT },
            entity   => { optional => 1 },
        },
    );

    my $location;
    if ( ref( $p{'location'} ) ) {
        $location = $p{'location'}->as_string;
    } else {
        $location = $p{'location'};
    }
    $c->response->status(201);
    $c->response->header( 'Location' => $location );
    $self->_set_entity( $c, $p{'entity'} );
    return 1;
}

=item status_accepted

Returns a "202 ACCEPTED" response.  Takes an "entity" to serialize.

Example:

  $self->status_accepted(
    $c,
    entity => {
        status => "queued",
    }
  );

=cut

sub status_accepted {
    my $self = shift;
    my $c    = shift;
    my %p    = Params::Validate::validate( @_, { entity => 1, }, );

    $c->response->status(202);
    $self->_set_entity( $c, $p{'entity'} );
    return 1;
}

=item status_bad_request

Returns a "400 BAD REQUEST" response.  Takes a "message" argument
as a scalar, which will become the value of "error" in the serialized
response.

Example:

  $self->status_bad_request(
    $c,
    message => "Cannot do what you have asked!",
  );

=cut

sub status_bad_request {
    my $self = shift;
    my $c    = shift;
    my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );

    $c->response->status(400);
    $c->log->debug( "Status Bad Request: " . $p{'message'} ) if $c->debug;
    $self->_set_entity( $c, { error => $p{'message'} } );
    return 1;
}

=item status_not_found

Returns a "404 NOT FOUND" response.  Takes a "message" argument
as a scalar, which will become the value of "error" in the serialized
response.

Example:

  $self->status_not_found(
    $c,
    message => "Cannot find what you were looking for!",
  );

=cut

sub status_not_found {
    my $self = shift;
    my $c    = shift;
    my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );

    $c->response->status(404);
    $c->log->debug( "Status Not Found: " . $p{'message'} ) if $c->debug;
  $self->_set_entity( $c, { error => $p{'message'} } );
    return 1;
}

sub _set_entity {
    my $self   = shift;
    my $c      = shift;
    my $entity = shift;
    if ( defined($entity) ) {
        $c->stash->{ $self->{'stash_key'} } = $entity;
    }
    return 1;
}

1;

APIを実装するサブクラス

package MyApp::Web::Controller::API::Articles;

use strict;
use warnings;
use base 'MyApp::Web::Controller::API';

=head1 DESCRIPTION

Catalyst Controller.

=head1 METHODS

=cut


=head2 index

=cut
sub list {
    my ($self, $c) = @_;

    // Implement me!

    $self->status_ok(
            $c,
            entity => {
                some => 'data',
                foo  => 'is real bar-y',
            },
    );

}

# POST /articles
sub create {
    my ($self, $c) = @_;
    my $article_data = $c->req->data;
    use Data::Dumper;
    warn Dumper $article_data;
    // Implement me!
}

# GET /articles/{article_id}
sub show {
    my ($self, $c, $article_id) = @_;
}

# PUT /articles/{article_id}
sub update {
    my ($self, $c, $article_id) = @_;
}

# DELETE /articles/{article_id}
sub destroy {
    my ($self, $c, $article_id) = @_;
}

# GET /articles/new
sub post {
    my ($self, $c) = @_;
}

# GET /articles/{article_id}/edit
sub edit {
    my ($self, $c, $article_id) = @_;
}


=head1 AUTHOR

dann,,,

=head1 LICENSE

This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

1;

簡単なテスト

GET

curl -X GET -H 'Content-Type: text/x-json' http://192.168.0.30:3000/articles

POST

curl -X POST -H 'Content-Type: text/x-yaml' -T article.yml http://192.168.0.30:3000/articles

これくらいしか動作確認してないけど... 元のコード丸ぱくりだから、まぁ動くんじゃないかしら。

TODO

  • content typeでAPIの入出力のフォーマットを切り替えるというのが流行っていない気もするけれど、その辺も気がむいたら調査しよう
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080320

2008-03-17

Catalystアプリのディレクトリ構造

21:52 |  Catalystアプリのディレクトリ構造 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystアプリのディレクトリ構造 - dann@catalyst  Catalystアプリのディレクトリ構造 - dann@catalyst のブックマークコメント

  Web
    Controller
    View
  Logic
  Schema
  CLI
    Command

Web以下にCatalyst関連のクラスは押し込める。Mattさんのパクリ。

Logicは、Webのコンテキストからは切り離す。ControllerとCLI/Commandのクラスから参照する。

SchemaとLogicの名前がいまいちかもしれないなぁ。MattさんはSchemaはDataStore or Domainという名前にしているみたい。大体、こんな感じで作っていけばいいかなぁという気もする。Logic切り離すところが、なんか綺麗に作れるといいなぁというところ。

Fixtureの読み込みとダンプ

| 21:30 |  Fixtureの読み込みとダンプ - dann@catalyst を含むブックマーク はてなブックマーク -  Fixtureの読み込みとダンプ - dann@catalyst  Fixtureの読み込みとダンプ - dann@catalyst のブックマークコメント

DBIx::Class::Fixtures

http://search.cpan.org/~lsaunders/DBIx-Class-Fixtures/lib/DBIx/Class/Fixtures.pm

ダンプもできるのがいい。

この前書いたモデルのテストのベースクラスの、fixtureを読み込む部分を書き換えることにしよう。

http://catalyst.g.hatena.ne.jp/dann/20080313/1205416454

テストデータは、DBIx::Class::Fixturesでダンプして作って、テストケース側では、それを読み込むと。ベーシックなやつは、fixtureは手書きで。そんな感じかな。

テスト用のデータベースクラスにFixtureのロード機能もまかせちゃって、テストケース側ではそのテスト用データベースクラスだけuseするっていうほうが、美しいかもしれないなぁ。そこはもう少し検討。

KerrynKerryn2011/12/27 20:55Which came first, the prboelm or the solution? Luckily it doesn't matter.

hfdcpsrqzshfdcpsrqzs2011/12/28 22:452lU7Ej , [url=http://hffosourukyv.com/]hffosourukyv[/url], [link=http://ivjsgtvhzfak.com/]ivjsgtvhzfak[/link], http://smraetuehzze.com/

gpzayorgpzayor2011/12/29 19:028nYjn5 <a href="http://ncmyvxwidlkt.com/">ncmyvxwidlkt</a>

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080317

2008-03-16

starter

14:47 |  starter - dann@catalyst を含むブックマーク はてなブックマーク -  starter - dann@catalyst  starter - dann@catalyst のブックマークコメント

テンプレートを別ファイルに分離したのを作ろうと思って、結局やってない。暇みつけてやろっと。

CLIの作成

14:42 |  CLIの作成 - dann@catalyst を含むブックマーク はてなブックマーク -  CLIの作成 - dann@catalyst  CLIの作成 - dann@catalyst のブックマークコメント

  • App::CLI, App::CmdのようなCLI作成のフレームワークを使う方法
  • App::Options
  • 標準モジュール

CLI用のフレームワークはCatalystアプリ用のCLIとして使うにはいいかなって気がする。もともと、多量のモジュールに依存するのが前提になっているので。App::CLIとApp::Cmdのどっちかがよいのかは分からなかったけれど、一応、App::Cmdでやりたいことは一通りできそう。コマンドの追加もとても楽。

App::Optionsは綺麗に書けるけれど、単体のスクリプトで配布するときには標準モジュールじゃないので都合が悪そう。その点で使いどころがどこになるのかが少し難しい。

標準モジュールは、極力モジュールに依存しないスクリプトを作るときに使う。

App::CLI, App::Cmd または 標準モジュール のいずれかを使うってことになるかなぁ。App::CLIとApp::Cmdがどっちがよいのかはよく分からない。コードはApp::CLIのほうが綺麗だけど、ドキュメントが...

Catalyst用のCLI作成

| 14:11 |  Catalyst用のCLI作成 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst用のCLI作成 - dann@catalyst  Catalyst用のCLI作成 - dann@catalyst のブックマークコメント

昨日のエントリのtomyheroさんのCatApp::CLIをベースに、

  • Catalystに依存しないでConfigを読む仕組み
  • CLIのスクリプトのコマンドを拡張する仕組み
  • CLIのコマンドからSchemaを参照する仕組み

を用意してみた。

Catalystに依存しないで使えるので、Catalyst外でも使えるし、App::CmdベースでCLIのコマンドそのもののテストができる。大分、テスタビリティも高いし、いいかなと。

これで、starterで一通りクラスを生成すれば、CLIを拡張していくときには、新規にMyApp::CLI::Commandを拡張していくだけになりました。

  • 簡単にコマンドが追加できること
  • コマンド中からSchemaとconfigを参照できること
  • Catalystに依存せずにCLIを作れること

という用件をひとまずは満たしたので、一応満足。

Schemaを直接参照ではなくて、ここでCというクラスを参照という形にするのがBetterなんだけれど、その仕組みを用意するには、ちと幾つか足りないのでまた今度。

コマンドライン用の管理スクリプト admin.pl

#!/usr/bin/env perl

use MyApp::CLI;
MyApp::CLI->new->run;

使うときは、

script/admin.pl list --open

など。

コマンドラインのコマンドの実装

CLIクラス
package MyApp::CLI;
use base qw(App::Cmd);

1;
コマンドのベースクラス

全てのコマンドでyamlschemaが参照できるようにする。コマンドラインのコマンドのベースクラス。

package MyApp::CLI::Command;
use base qw/App::Cmd::Command/;
use MyApp::ConfigLoader;
use MyApp::Schema;

sub config {
    my $self = shift;
    $self->{config} ||= MyApp::ConfigLoader->new->config;
    return $self->{config};
}

sub schema {
    my $self = shift;
    $app->{schema}  = MyApp::Schema->connect( @{ $self->config->{'Model::DBIC'}{'connect_info'} } );
}

1;
コマンドのクラス

後は、上記のベースクラスを継承して、実装を追加すればコマンドラインオプションが追加できる。

package MyApp::CLI::Command::List;
use base qw/MyApp::CLI::Command/;

=head1 NAME
MyApp::CLI::Command::List - list events
=cut

sub opt_desk {
    return (
        ["open", "only unfinished events"],
    );
}

sub validate_args {
    my ($self, $opt, $args) = @_;
    # we need at least one argument beyond the options
    die $self->usage->text unless @$args;
}

sub run {
    my ($self, $opt, $args) = @_;
    use Data::Dumper;
    warn Dumper $self->schema;
    warn Dumper $self->config;
}

1;

Catalystに依存しないconfigの読み込み

14:11 |  Catalystに依存しないconfigの読み込み - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないconfigの読み込み - dann@catalyst  Catalystに依存しないconfigの読み込み - dann@catalyst のブックマークコメント

Catalystに依存せずにyamlなどの設定を読むようにした。

package MyApp::ConfigLoader;

use strict;
use warnings;

use MyApp::Utils;
use Config::Any;
use File::Spec;

sub new {
    my $class = shift;
    my $self = {};
    bless $self , $class;
    $self->{config} = $self->load;
    return $self;
}

# looks ugly. Fix later
sub app_name {
    'MyApp::Catalyst';
}

sub prefix {
    my $self = shift;
    my $prefix = MyApp::Utils::appprefix( $self->app_name );
    return $prefix;
}

sub config {
    my $self = shift;
    return $self->{config};
}

sub load {
    my $self = shift;
    my @files = $self->find_files;
    my $cfg   = Config::Any->load_files(
        {   files       => \@files,
            use_ext     => 1,
        }
    );

    my $config = {};
    my $config_local = {};
    my $local_suffix = $self->get_config_local_suffix;
    for ( @$cfg ) {

        if ( ( keys %$_ )[ 0 ] =~ m{ $local_suffix \. }xms ) {
            $config_local =  $_->{ (keys %{$_})[0] };
        }
        else {
            $config = {
                %{ $_->{ (keys %{$_})[0] }},
                %{$config} ,
            }
        }
    }

    $config = {
        %{$config},
        %{$config_local} ,
    };
    return $config;
}

sub local_file {
    my $self = shift;
    my $prefix = $self->prefix;
    return File::Spec->catfile($self->get_config_dir_path, $prefix . '_' . $self->get_config_local_suffix);
}

sub find_files {
    my $self = shift;
    my ( $path, $extension ) = $self->get_config_path;
    my $suffix     = $self->get_config_local_suffix;
    my @extensions = @{ Config::Any->extensions };

    my @files;
    if ( $extension ) {
        next unless grep { $_ eq $extension } @extensions;
        ( my $local = $path ) =~ s{\.$extension}{_$suffix.$extension};
        push @files, $path, $local;
    }
    else {
        @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
    }

    return @files;
}

sub get_config_dir_path {
    my $self = shift;
    my $home = MyApp::Utils->home;
    return File::Spec->catfile( $home , 'conf', $self->prefix . ".yml");

}

sub get_config_path {
    my $self = shift;
    my $path = $self->get_config_dir_path;
    my $extension = 'yml';
    return ( $path, $extension );
}

sub get_config_local_suffix {
    my $self = shift;
    my $suffix = MyApp::Utils::env_value( $self->app_name, 'CONFIG_LOCAL_SUFFIX' ) || "local";
    return $suffix;
}

1;

MyApp::Utils

Catalyst::Utilsの必要なところだけ、Catalystに依存しないように抽出。

package MyApp::Utils;

use strict;
use File::Spec;
use Path::Class;
use URI;
use Class::Inspector;
use Carp qw/croak/;

sub appprefix {
    my $class = shift;
    $class =~ s/::/_/g;
    $class = lc($class);
    return $class;
}

sub class2appclass {
    my $class = shift || '';
    my $appname = '';
    if ( $class =~ /^(.+?)::([MVC]|Model|View|Controller)::.+$/ ) {
        $appname = $1;
    }
    return $appname;
}

sub class2classprefix {
    my $class = shift || '';
    my $prefix;
    if ( $class =~ /^(.+?::([MVC]|Model|View|Controller))::.+$/ ) {
        $prefix = $1;
    }
    return $prefix;
}

sub class2classsuffix {
    my $class = shift || '';
    my $prefix = class2appclass($class) || '';
    $class =~ s/$prefix\:://;
    return $class;
}

sub class2env {
    my $class = shift || '';
    $class =~ s/::/_/g;
    return uc($class);
}

sub class2prefix {
    my $class = shift || '';
    my $case  = shift || 0;
    my $prefix;
    if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
        $prefix = $case ? $2 : lc $2;
        $prefix =~ s{::}{/}g;
    }
    return $prefix;
}

sub class2tempdir {
    my $class  = shift || '';
    my $create = shift || 0;
    my @parts = split '::', lc $class;

    my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;

    if ( $create && !-e $tmpdir ) {

        eval { $tmpdir->mkpath };

        if ($@) {
            # FIXME
            #MyApp::Exception->throw(
            #    message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
        }
    }

    return $tmpdir->stringify;
}

sub home {
    my $class = shift;

    # make an $INC{ $key } style string from the class name
    (my $file = "$class.pm") =~ s{::}{/}g;

    if ( my $inc_entry = $INC{$file} ) {
        {
            # look for an uninstalled Catalyst app

            # find the @INC entry in which $file was found
            (my $path = $inc_entry) =~ s/$file$//;
            my $home = dir($path)->absolute->cleanup;

            # pop off /lib and /blib if they're there
            $home = $home->parent while $home =~ /b?lib$/;

            # only return the dir if it has a Makefile.PL or Build.PL
            if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")) {

                # clean up relative path:
                # MyApp/script/.. -> MyApp

                my ($lastdir) = $home->dir_list( -1, 1 );
                if ( $lastdir eq '..' ) {
                    $home = dir($home)->parent->parent;
                }

                return $home->stringify;
            }
        }

        {
            # look for an installed Catalyst app

            # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
            ( my $path = $inc_entry) =~ s/\.pm$//;
            my $home = dir($path)->absolute->cleanup;

            # return if if it's a valid directory
            return $home->stringify if -d $home;
        }
    }

    # we found nothing
    return 0;
}

sub prefix {
    my ( $class, $name ) = @_;
    my $prefix = &class2prefix($class);
    $name = "$prefix/$name" if $prefix;
    return $name;
}

sub env_value {
    my ( $class, $key ) = @_;

    $key = uc($key);
    my @prefixes = ( class2env($class), 'CATALYST' );

    for my $prefix (@prefixes) {
        if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
            return $value;
        }
    }

    return;
}

1;

lcipavlcipav2011/03/24 10:164IGWLn <a href="http://glbzfqoqlaox.com/">glbzfqoqlaox</a>, [url=http://ikimzilitduw.com/]ikimzilitduw[/url], [link=http://koddoycduxbb.com/]koddoycduxbb[/link], http://licjkrlymalo.com/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080316

2008-03-15

Catalyst appでCLI

| 04:08 |  Catalyst appでCLI - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst appでCLI - dann@catalyst  Catalyst appでCLI - dann@catalyst のブックマークコメント

tomyhero++

http://d.hatena.ne.jp/tomyhero/20080315/1205598628

Catalyst::Utilsへの依存を切って、後は自分の構成にあわせれば殆どそのまんま使えそう! App::Cmdと組み合わせてベースクラスを作ろうかな。tomyheroさんのやつをCommandのベースクラスに押し込んでしまえばいい気がする。後で作って、starterに組み込む!

App::Cmdは、以下のスライドが分かりやすい。http://www.slideshare.net/rjbs/writing-modular-commandline-apps-with-appcmd/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080315

2008-03-13

モデルのfunctional test

| 22:54 |  モデルのfunctional test - dann@catalyst を含むブックマーク はてなブックマーク -  モデルのfunctional test - dann@catalyst  モデルのfunctional test - dann@catalyst のブックマークコメント

DBICx::TestDatabaseとTest::Fixture::DBIC::Schemaを使ってモデルのテストをしてみた。大体イメージどおりの感じで使えるかなぁと。

テスト用のDBクラス

DBICx::TestDatabaseを使っている。将来的に拡張する可能性があるため継承をしておく。

package MyApp::Test::DBIC::Database;
use strict;
use warnings;
use base qw(DBICx::TestDatabase::Subclass MyApp::Schema);

1;

モデルのテスト用のベースクラス。

setupでfixture作成. Test::Fixture::DBIC::Schemaでfixtureのロード.

package MyApp::Test::Model::Base;
use strict;
use warnings;
use base qw/Test::Class/;
use MyApp::Test::DBIC::Database;
use Test::Fixture::DBIC::Schema;
use File::Spec;

sub setup: Test(setup) {
    my $self = shift;
    $self->_make_fixture($self->model,$self->fixture);
}

sub _make_fixture {
    my $self = shift;
    my $model = shift;
    my $fixture = shift;

    $self->{schema} = MyApp::Test::DBIC::Database->connect();
    my $fixture_path = File::Spec->catfile('t','fixtures', $self->fixture . ".yaml");
    my $data = construct_fixture(
        schema  => $self->{schema},
        fixture => $fixture_path,
    );
    $self->{model} = $self->{schema}->resultset($model);
}

sub teardown : Test(teardown) {
};

sub fixture {
    my $self = shift;
    die "need to override";
}

sub model {
    my $self = shift;
    die "need to override";
}

1;

テストコード

モデルのテストクラスは、MyApp::Test::Model::Baseを継承する。fixtureとmodelメソッドをオーバーライドすると、該当するfixtureがロードされる。各テストメソッド間では、テーブルの内容は削除されるので、メソッド間に依存関係はない。

#!perl
use strict;
use warnings;

MyApp::Test::Model::Feeds->runtests;

package MyApp::Test::Model::Feeds;
use base qw/MyApp::Test::Model::Base/;
use Test::More;

sub test_feed : Tests {
    my $self = shift;

    my $feed = $self->{model}->find(1);
    is $feed->id, 1;
    is $feed->feedlink, "http://example.net/rss";
}

sub fixture {
    return "feeds";
}

sub model {
    return "Feeds";
}

1;

fixture

t/fixtures/feeds.yaml

t/fixturesに置くのは、Railsのパクリ。

- schema: Feeds
  name: entry1
  data:
    id: 1
    feedlink: http://example.net/rss
    link: http://example.net
    title: Example
    description: This is Example site
    subscribers_count: 1

- schema: Feeds
  name: entry2
  data:
    id: 2
    feedlink: http://example2.net/rss
    link: http://example2.net
    title: Example2
    description: This is Example2 site
    subscribers_count: 2

検討事項

  • SQLiteでテストする必要がなければ、Test::Fixture::DBIC::Schemaだけでいいなぁ。ここらはSQLiteでのテストが必要かも含めて少し検討かな。
    • モデルのテストはSQLiteだけ、結合テストのときだけMySQLでという選択肢もありえるかもしれない。
  • DB使わないでモデルをテストするユニットテスト用のベースクラスも作成する
  • fixture作るのが面倒. SQLでデータ流し込んで、YAMLDBからDumpする仕組みを用意したほうがいいかも

CarrieCarrie2011/08/02 22:59This website makes tghins hella easy.

uqkmntyamuqkmntyam2011/08/04 23:04LV0GWW , [url=http://ssntczkglidm.com/]ssntczkglidm[/url], [link=http://ybwyazadkvxq.com/]ybwyazadkvxq[/link], http://tnhbhzdyqjob.com/

xixywzesscxxixywzesscx2011/08/05 22:32WrlimC , [url=http://uxhjuacepxav.com/]uxhjuacepxav[/url], [link=http://mgietljruzer.com/]mgietljruzer[/link], http://tumkfemdvdet.com/

2008-03-12

Perlの文化

22:19 |  Perlの文化 - dann@catalyst を含むブックマーク はてなブックマーク -  Perlの文化 - dann@catalyst  Perlの文化 - dann@catalyst のブックマークコメント

TAKESAKOさんのブクマコメントは、端的にPerl文化を言い表しているなぁと思った。

いろんな人の書いたライブラリやフレームワークを平等に歓迎し、使う人がその用途によって最適なものを選ぶ。それがCPAN文化。選択肢が多いと初心者の迷いが多くなるが、使う人が賢くなるキッカケを与える。それがPerl文化

Catalystのプラグインシステムによる多様性の良さも、まさにここにあるんだと思う。他の言語でこういうフレームワークは他にないと思うし、そこに魅力がある。

SocialGraphでPerlの情報収集

22:12 |  SocialGraphでPerlの情報収集 - dann@catalyst を含むブックマーク はてなブックマーク -  SocialGraphでPerlの情報収集 - dann@catalyst  SocialGraphでPerlの情報収集 - dann@catalyst のブックマークコメント

SocialGraphが普及すると、SocialGraphで関連のある人のBlogの一覧を出して、そのBlogRSSを一括でRSS Readerに登録するなんてことができるかもしれない。こうすると、情報収集の効率が何倍もよくなりそう。今は、殆どこれを人手でやってるけど、基本的に手動でやる必要ないんだよなぁと。

こうなっていくと、Perlの情報に限らず、何だって簡単に情報が得られるようになる。密にコミュニティにかかわっていなくても、割に情報が得やすくなるかもしれない。そうすると、高速道路の車線が増えるようになり、高速道路は一般人でも簡単に乗れるようになるかもしれない。そうすると、情報収集のスタイルは少し変わっていきそうだな。

と思って、ためしにjrockwayのvoxblogで試してみた!

http://socialgraph-resources.googlecode.com/svn/trunk/samples/findyours.html?q=http%3A%2F%2Fjrock.vox.com%2Fexplore%2Ffriends%2F

結果からいうと、そういう未来は数年後。

DBICのモデルのテスト

| 22:01 |  DBICのモデルのテスト - dann@catalyst を含むブックマーク はてなブックマーク -  DBICのモデルのテスト - dann@catalyst  DBICのモデルのテスト - dann@catalyst のブックマークコメント

jrockway++

http://search.cpan.org/~jrockway/DBICx-TestDatabase-0.01/lib/DBICx/TestDatabase.pm

functional testの一部として使えそう!後で試す。

海外のPerler情報の集め方

| 21:58 |  海外のPerler情報の集め方 - dann@catalyst を含むブックマーク はてなブックマーク -  海外のPerler情報の集め方 - dann@catalyst  海外のPerler情報の集め方 - dann@catalyst のブックマークコメント

from id:charsbarさん

これはありがたい!

自分は、大体以下のように、ある信頼できる人が登録しているネットワークごと丸ごとみるみたいな形で情報を集めることが多いぁと。信頼できる人が形成するネットワークはまた同様に信頼できる可能性が高いと思っていて、それで情報を集めると。

ただ、これだと日本の情報に偏りがちなので、http://del.icio.us/network/<perlhacker>みたいな形で、海外のPerl関連情報を集められたらなぁって思ってるんですが、この入り口のポイントが分からなくて、情報の収集効率がかなり悪いなぁと。

まずは、id:charsbarさんのリンクからたどって、地味に信頼できる人を見つけようかなぁと思ってます。

snippetのdictionary生成

| 21:49 |  snippetのdictionary生成 - dann@catalyst を含むブックマーク はてなブックマーク -  snippetのdictionary生成 - dann@catalyst  snippetのdictionary生成 - dann@catalyst のブックマークコメント

snippetsEmuをなかなか使いこなせていないのは、どんなsnippetsがあるかを覚えられないからだという当たり前のことに気づいた。ということで、snippetsから、dictionaryを作ればいいんじゃないかと思った。

dictionaryにsnippetのキーワードが入っていれば、autocomplで候補が表示されるので、snippetsの補完ができるようになる。(なんだか、snippetsEmuの使い方を間違えているのかもしれないけれど...)。

ということで、以下がsnippetsからdictを生成するスクリプト。

使い方は、

snippets2dict.pl perl > perl.dict

以下がsnippets2dict.plのコード。

#!/usr/bin/perl
use strict;
use warnings;

use Carp;
use File::Find;

my $langtype = $ARGV[0];
unless($langtype) {
    die "langtype is required. Usage: snippets2dict.pl perl";
}

my $dir = "$ENV{'HOME'}/.vim/after/ftplugin";
unless(-d $dir) {
    die "$dir isn't exist"
}

find(\&print_file, $dir);

sub print_file {
    if($File::Find::name =~ /([a-zA-Z0-9\-]+)_snippets\.vim$/) {
        my $filetype = $1;

        my $file_path = $File::Find::name;
        my @contents = get_contents($file_path);
        foreach my $line (@contents) {
            if($line =~/Snippet\s+(.+?)\s+/i) {
                if($langtype eq $filetype) {
                    print $1 . "\n";
                }
            }
        }
    }
}

sub get_contents {
    my $file = shift;

    my $fh;
    open $fh, '<', $file or Carp::confess( "unable to open $file: $!" );

    if (wantarray) {
        my @contents = <$fh>;
        close $fh;
        return @contents;
    }

    my $contents = do { local $/; <$fh> };
    close $fh;
    return $contents;
}

# catalyst系のsnippetも、これで使えるように!

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080312

2008-03-09

Perlでの情報のキャッチアップの大変さとPerlを使うことの面白さ

03:07 |  Perlでの情報のキャッチアップの大変さとPerlを使うことの面白さ - dann@catalyst を含むブックマーク はてなブックマーク -  Perlでの情報のキャッチアップの大変さとPerlを使うことの面白さ - dann@catalyst  Perlでの情報のキャッチアップの大変さとPerlを使うことの面白さ - dann@catalyst のブックマークコメント

PerlCPANモジュールもたくさんあるし、何使えばいいのかが最初なかなかわからない。Plaggerがなかったら、どのモジュール使うのが一般的なのかも、結構密にコミュニティに入ったりフレームワークのコード追っかけてたりしないとわからない。

Javaだと、言語標準でそこそこ機能があるのと、ライブラリは殆どApacheCodeHausにあって殆ど選択で悩むことがない。この辺が、Perl界隈でGeekな人と他の人との間に差ができる要因の一つなんだろうなって気がする。

それと、まだ海外のgeekな人で活発にblog書いてる人ってのがいまいちわかってなくて、情報量に大分差がある気がするなぁって気がする。まずは、どんな言語やるんでも多量の情報のインプットがやっぱり必要だなぁと。まだインプットが全然足りない気がする。Matt Trout、Jessie Vincent、Jonathan Rockway, STの人あたりのBlogとかMLでの話ししか見てないのだけれど、もっと見たほうがよいところはありそうだなぁ。

ただ、Perl界隈は日本のコミュニティがとても活発だし、日本のコミュニティだけ見てても面白い。楽しいことをやろうっていう雰囲気があって、そこが魅力的だと思う。miyagawaさんをはじめ、日本人hackerも多いし、blogおっかけてても楽しい。それと、多量のCPANモジュールがあって、いいコードを見るのに勉強できる環境があるのがいい。

dmakiさんみたいなhackerでも精進が必要と言っているわけで、自分みたいにまだ「猛烈なアウェー感」がある人は、もっと勉強しないといけないなと思うわけです。折角、lazy-peopleにも入ったことだし、色々と教えてもらいながら勉強してこうかなぁって思う。何かフィードバックできるところはしていきながら、何かしていきたいなと。

多量のインプットをするのと、多量のインプットを納得できるレベルまで理解するのを頑張りたい。人の意見をたくさん聞くのと、それを自分で仮説をたてて何回も理解できるまで解釈するのを今年は続けていこうかなと。今年の後半には、何かアウトプットが出せるようにするのを目標になんかやっていきたいなと。

Web周りで何かするならPerl界隈が一番面白いと思っているし、頑張りたいところです。そんなこんなで、Catalyst周りをしばらく頑張っていきます。

#catalyst-jaを外から見るには

01:23 |  #catalyst-jaを外から見るには - dann@catalyst を含むブックマーク はてなブックマーク -  #catalyst-jaを外から見るには - dann@catalyst  #catalyst-jaを外から見るには - dann@catalyst のブックマークコメント

tiarramobircを使えばいいよと、id:woremacxさんに教えてもらった。これは後でやる。thanks!

みんなで開発

00:27 |  みんなで開発 - dann@catalyst を含むブックマーク はてなブックマーク -  みんなで開発 - dann@catalyst  みんなで開発 - dann@catalyst のブックマークコメント

virtualなグループで開発できる環境みたいなのを用意できたら楽しいですねぇと思って、ちょっとやってみたら、いろいろハマッた...

自分だけ操作できて、他の人にreadonlyにするときに-wの前にスペースが必要ないのと、acladdがカンマ区切りなのでハマった orz...

.screenrc

multiuser on
aclumask tomyhero,woremacx,hideden,vkgtaro-w
acladd tomyhero,woremacx,hideden,vkgtaro,dann

ウィンドウのサイズは、セッションに参加する人で共通にしておかないといけない。80x24だと狭いので、120x48位にしておくといいかも。puttyだと「設定の変更->ウィンドウ」で変更する。

あと、当たり前なんだけど、全員でキーバーインドが違うとかだと、なかなかカオスだからzshとscreenくらいは一部の設定を共通化しないとダメだなぁということ。開発環境の共有というのは会社でえいやーって決めれないとうまく進めにくいかも。エイヤで共有してみるのも、面白いかもしれないけれど。vim, emacsの設定は別にして。

# catalystに関係ない話題を書く場所があるといいかなぁ。自分の日記に書こうか、それともlazy-people用のgroup作るのがいいかな。

lazy-peopleにjoin

17:42 |  lazy-peopleにjoin - dann@catalyst を含むブックマーク はてなブックマーク -  lazy-peopleにjoin - dann@catalyst  lazy-peopleにjoin - dann@catalyst のブックマークコメント

http://lazy-people.org/

「erogeekになりたいので、是非参加させてください!」とお願いしたところ、「全裸でおっけい」という回答をいただいたので参加することにしました。erogeekになるべくCatalystの勉強を進めていきたいと思います。

# はてスタつきすぎですw

多数のCPANモジュールに依存したプロダクトを正しくShipするには?

| 14:18 |  多数のCPANモジュールに依存したプロダクトを正しくShipするには? - dann@catalyst を含むブックマーク はてなブックマーク -  多数のCPANモジュールに依存したプロダクトを正しくShipするには? - dann@catalyst  多数のCPANモジュールに依存したプロダクトを正しくShipするには? - dann@catalyst のブックマークコメント

ということに対する解を提供しようとしているのが、Shipwrightなのかなと。だから、Ship rightなんじゃないかと。wが何なのかはしらない...

どんな時に使えそうかっていうと、

  • プロダクトをリリースするときに、プロダクトに依存モジュールを「指定したバージョン」で一括してインストールしたい場合
    • プロダクト使っている場合って、途中からインターフェースが変わっちゃってたりとか、そのモジュールではバグがあって、リリースできないなんてケースはままあるので、リリース時点で、プロダクトがどのバージョンのどのモジュールに依存しているのかを正確に管理したいと思うんですよね。
  • 開発サーバーが複数あって、各種サーバーでのCPANモジュールのバージョンをあわせたい場合
    • cpanコマンドでインストールするのだと、CPANモジュールのバージョンがずれるケースがあるよねと。だから、shipwright用のレポジトリをつかっておけば、一括でインストールできると

これが進んでいくと、CPANモジュールに多数依存したプロダクトは、Shipwrightのレポジトリ形式でリリースされることになるんじゃないかなと。

今のCPANだけだと、プロダクトをリリースするには、モジュールのバージョンを管理するための機能が足りなくって、だから、プロダクトやサービスを作るためには、モジュールをrmp化したり、deb化したりする必要があるなと。

Shipwrightが良さそうに見えるのは、プロダクトに依存したモジュールをバージョン管理下において、指定したバージョンのモジュールを一括でリリースできそうなので、上記に書いたモジュールのバージョン問題を解決してくれるんじゃないかという気がしているから。

そうすると、下記の2手順で、プロダクトをインストールできるようになるんじゃないかと。

  • ディストリビューションのライブラリ(CPANモジュールが必要なライブラリ)のインストール
  • Shipwrightライトでのプロダクトのインストール

Shipwright自身は、Shipwrightのレポジトリだけで、プロダクトをインストールできるようにすることが最終ゴールのようにみえなくもないけれど、使い込んでないのでそれはおいとこう... モジュールに依存するパッケージ(deb, rpm)などを指定するようなことは、パッと見はかいてなかったので、別の方法でやんのかな。

で、Catalystを使ったアプリは、Shipwright形式で提供するというには適合しているんじゃないかなと。他にもPlaggerとかも。

FauzhenFauzhen2012/10/31 14:11That's a gneuinely impressive answer.

qtckesjsusqtckesjsus2012/11/02 06:52EVqZvT , [url=http://xndboidvsrqx.com/]xndboidvsrqx[/url], [link=http://chyncggpmptm.com/]chyncggpmptm[/link], http://vzsvxwkggowa.com/

adqfbppznkaadqfbppznka2012/11/03 11:33AXNUDI , [url=http://fmquxcojtqow.com/]fmquxcojtqow[/url], [link=http://xefledttvdkc.com/]xefledttvdkc[/link], http://tpenlutzsdiq.com/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080309

2008-03-08

PerlでのWebアプリケーションのテストツール

12:25 |  PerlでのWebアプリケーションのテストツール - dann@catalyst を含むブックマーク はてなブックマーク -  PerlでのWebアプリケーションのテストツール - dann@catalyst  PerlでのWebアプリケーションのテストツール - dann@catalyst のブックマークコメント

Webアプリに限定しているわけではないですが、まとめてみました。

単体テスト

  • Test::Class
  • Test::Base
    • データドリブンのテスト
  • Test::More

Mock

  • mocked
    • Railsのtest/mocks的なもの
  • Test::MockObject ?

カバレッジテスト

  • Devel::Cover

機能テスト

結合テスト

  • Socialtext::WikiTest
    • SeleniumRC+Wikiでのテストに使う。名前空間があれだけれど、Socialtextに依存はしていない。Fixtureだけ各Wiki用に用意すればよい。
  • Test::WWW::Selenium
    • PerlでSeleniumRCをhandle

Smoke test

  • Test::Chimps ?

ファイル変更時の自動テスト

  • Test::Continuous

Continuous Integration

  • buildbot

その他

  • Test::Less
    • 特定のテストスクリプトに tag をつけて特定の範囲だけのテストを実行する

See also:

http://qootas.org/blog/archives/2006/08/post_30.html

    * Test::Chimps をつかってどのリビジョンでどのエラーが発生しているのかを記録
    * Test::Base でテストコードをデータドリブンで書きやすくする
    * Test::Less をつかって特定のテストスクリプトに tag をつけて特定の範囲だけのテストを実行する

疑問点

  • Perl界隈での標準的なMockライブラリがよくわかってない。Test::MockObject あたりかな?
  • Test::Lessは、便利そうなんだけれど、特定範囲という場合の特定範囲のtagにどんなものがありうるのかが、まだわからないなぁ。ここはアプリケーション依存だとは思うのだけれど。

TODO

  • Test::Chimpsを使ってみる。Smoke Testの需要はある気がする。ただ、smoke testだけじゃなくて、buildbot使うだけでいいかもしれない。

いいContinuous Integrationツールは? その2

22:13 |  いいContinuous Integrationツールは? その2 - dann@catalyst を含むブックマーク はてなブックマーク -  いいContinuous Integrationツールは? その2 - dann@catalyst  いいContinuous Integrationツールは? その2 - dann@catalyst のブックマークコメント

buildbotは複数チームで複数ブランチで平行開発しているときには、複数ブランチのテスト結果が一覧でみれるのでいいかなぁと思うんですが、やっぱりテスト結果の見え方がいまいちだなぁと。

ということで、もう少し探してみました。そこで見つけたのが、smolderです。

http://sourceforge.net/projects/smolder

Web-Based Smoke Test Aggregator used by developers and testers to upload (automated or manually) and view smoke/regression tests using the Test Anything Protocol. Details and trends can be viewed in various formats. Notifications of runs and failures via

以下で簡単に紹介されています。

http://www.slideshare.net/mpeters/smolder-introduction

ここで書かれている課題が一番解決したい課題かなぁと。画面イメージを見た感じでも、自分が解決したい課題を解決してくれるツールかなって気がします。ちょっと後で試してみようかな。

 PerlのDIコンテナ - Bread Board

21:08 |  PerlのDIコンテナ - Bread Board - dann@catalyst を含むブックマーク はてなブックマーク -  PerlのDIコンテナ - Bread Board - dann@catalyst  PerlのDIコンテナ - Bread Board - dann@catalyst のブックマークコメント

http://search.cpan.org/~stevan/Bread-Board-0.03/

依存関係を設定するところが一箇所にまとまってしまうところが、ちょっといまいちかなぁという気がしました。Javaだと1世代前のDIコンテナにあたるかなと。

JavaのDIコンテナも、最初はこんな感じで、依存関係を設定するところが全てXMLで記述されていたんですが、これはとてもよくなかったなと思っていました。XMLだったのがよくなかったというのもあるんですが、依存関係を設定する部分が、実際の依存関係をインジェクトされる側との距離が遠すぎるというのが一番良くなかったなと思っています。

最近のDIコンテナは、依存関係のインジェクションが必要なクラスに、JavaのAnnotationでメタデータを記述するようになっているので、該当のクラスをみれば、どのようなものがインジェクトされるのか、インジェクトされるもののScopeが何なのかというのがすぐにわかるようになっています。

Annotaionは、PerlだとAttribteに近いのかもしれません。あんまり、Attributeのことをわかってないかもしれませんが...

# yappoさんの記事がわかりやすそうなので、後で見る。

http://blog.yappo.jp/yappo/archives/000348.html

まだ、0.03なので、どのような方向で進むかはわかりませんが、DIコンテナとして使うには時期尚早かなという気がしました。当面必要なのは、Configurationをフレームワークに依存しない形で参照すればいいだけなので、それでいこうかなと。

Catalystアプリの開発で使える周辺ツール

20:48 |  Catalystアプリの開発で使える周辺ツール - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystアプリの開発で使える周辺ツール - dann@catalyst  Catalystアプリの開発で使える周辺ツール - dann@catalyst のブックマークコメント

今のところ、以下のものですね。

capistranoもbuildbotも、言語を選ばずに使えそうなのと、出来はよさそうなのでもう少し使い込もうかなと思ってます。capistranoruby, buildbotはpythonと、Perlとはあまり関係ないんですが、他の言語を使うのも楽しいのでそれでいいかなと。

いいCIツールは?

20:40 |  いいCIツールは? - dann@catalyst を含むブックマーク はてなブックマーク -  いいCIツールは? - dann@catalyst  いいCIツールは? - dann@catalyst のブックマークコメント

Perl界隈で何かいいCIツールがあれば、それを使ってみようかと思ったのですが、さっと探した感じではなさそうだったので、buildbotを使ってみることにしました。pythonで記述されてますが、python専用のCIツールではないです。使ってみた感じだと、一通りCIツールとしての機能はもっているかなという印象です。

他に何かいいツールがあれば、だれかエロイ人教えてください!

# miyagawaさんからTest::Continuousを教えてもらったのですが、ちょっと自分が思っているCIツールとは違うかなぁと。Test::Continuousは、ZenのAutotestと殆ど同じで、ファイル変更タイミングでテストが走る仕組みなので、個人で使うツールかなぁと。スケジュールビルドをして、日に1-2回の単位でデグレードをチェックをして、チーム全体で結果を共有するっていう用途で使いたいなぁと。

Continuous Integrationのイメージは大体以下のページに書かれています。

http://www.martinfowler.com/articles/continuousIntegration.html

JavaだとContinuumとかCruiseControlにあたるものかなぁと。

複数ブランチのビルド結果を一覧できるbuildbot

| 20:40 |  複数ブランチのビルド結果を一覧できるbuildbot - dann@catalyst を含むブックマーク はてなブックマーク -  複数ブランチのビルド結果を一覧できるbuildbot - dann@catalyst  複数ブランチのビルド結果を一覧できるbuildbot - dann@catalyst のブックマークコメント

とある会社で開発していたときは、15人程度で開発をやっていました。複数の開発チームに分かれて、開発チームごとにブランチが切り、平行して開発を進めていました。その会社ではCIツールは自作していたのですが、全体の開発のブランチの状況が一覧できなかったのは、よくなかったなと思いました。

buildbotでは、各ブランチのビルド・テスト結果が一覧してみれるので、なかなかいいなと思っています。Windows上でも動作するようなので、複数のプラットフォームでテストするようなプロダクトや、複数ブランチで平行して開発しているようなところでは結構使えるんじゃないかと思いました。

これが、割と多くのプロジェクトで使われている理由なのかなという気もします。

CatalystアプリをbuildbotでContinuous Integration

| 20:29 |  CatalystアプリをbuildbotでContinuous Integration - dann@catalyst を含むブックマーク はてなブックマーク -  CatalystアプリをbuildbotでContinuous Integration - dann@catalyst  CatalystアプリをbuildbotでContinuous Integration - dann@catalyst のブックマークコメント

buildbotはPythonで記述されたCIツールで、割と多くのプロジェクトで使われているようです。

http://buildbot.sourceforge.net/manual-0.7.5.html

どのような形で結果が見えるのかは、djangoのページなどを参照するのがわかりやすいです。

http://buildbot.djangoproject.com/

buildbotでは、スケジュールに応じてビルド・テストなどを行い、ビルド・テストの結果をメールやIRCなどで通知させることができます。CIツールはテスト数が多くなってきたときに活躍します。日に数回、全体の単体テスト・機能テストを全て実行するようにしておくと、プロダクトのデグレードは減ります。

前置きはおいといて、実際にbuildbotの設定方法を見ていきましょう。

インストール

sudo aptitude install buildbot

マスターの作成

% buildbot create-master ~/Buildbot/master/MyApp

マスターの設定

% vim ~/Buildbot/master/MyApp/master.cfg

master.cfgには、大きく分けて、大体以下の4つの設定をします

  • sourcesに、何をチェック対象にするかを記述
  • scheduleに、どのようなタイミングでsourceをチェックするかを設定
  • buildersに、ビルドやテストのステップを記述
  • statusに、ビルド結果の通知方法を記述

master.cfgは、以下のような感じに書きます。

# -*- python -*-
# ex: set syntax=python:

# This is a sample buildmaster config file. It must be installed as
# 'master.cfg' in your buildmaster's base directory (although the filename
# can be changed with the --basedir option to 'mktap buildbot master').

# It has one job: define a dictionary named BuildmasterConfig. This
# dictionary has a variety of keys to control different aspects of the
# buildmaster. They are documented in docs/config.xhtml .


# This is the dictionary that the buildmaster pays attention to. We also use
# a shorter alias to save typing.
c = BuildmasterConfig = {}

####### BUILDSLAVES

# the 'bots' list defines the set of allowable buildslaves. Each element is a
# tuple of bot-name and bot-password. These correspond to values given to the
# buildslave's mktap invocation.
c['bots'] = [("MyApp-bot","buildbot")]


# 'slavePortnum' defines the TCP port to listen on. This must match the value
# configured into the buildslaves (with their --master option)

c['slavePortnum'] = 9989

####### CHANGESOURCES
# the 'sources' list tells the buildmaster how it should find out about
# source code changes. Any class which implements IChangeSource can be added
# to this list: there are several in buildbot/changes/*.py to choose from.

from buildbot.changes.svnpoller import SVNPoller
source_code_svn_url='http://192.168.0.30/svn/MyApp/trunk'
svn_user='buildbot'
svn_password='password'

svn_poller = SVNPoller(
    svnurl=source_code_svn_url,
    svnuser=svn_user,
    svnpasswd=svn_password,
    pollinterval=60*1, #seconds
    histmax=10,
    svnbin='/usr/bin/svn',)

c['sources'] = [ svn_poller ]

####### SCHEDULERS

## configure the Schedulers
# When to run builds
from buildbot import scheduler
daily = scheduler.Periodic("daily", ["MyApp-Builder"], 24*60*60)
code_changed = scheduler.Scheduler(name="code_changed",
    branch=None,
    treeStableTimer=10*60,
    builderNames=["MyApp-Builder"])
c['schedulers'] = [ daily,code_changed ]

####### BUILDERS
# How to build the software
builders = []

from buildbot.process import factory
from buildbot.steps import source,shell
f1 = factory.BuildFactory()
f1.addStep(source.SVN,
    svnurl=source_code_svn_url,
    mode="clobber")
f1.addStep(shell.ShellCommand, command=['perl', 'Makefile.PL'])
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

b1 = {'name': 'MyApp-Builder',
    'slavename': 'MyApp-bot',
    'builddir': 'myapp_build_dir',
    'factory': f1,}
c['builders'] = [b1]

####### STATUS TARGETS
# 'status' is a list of Status Targets. The results of each build will be
# pushed to these targets. buildbot/status/*.py has a variety to choose from,
# including web pages, email senders, and IRC bots.


from buildbot.status import html
from buildbot.status import words
htmlStatus = html.Waterfall(http_port=8010)
ircStatus = words.IRC(host="irc.example.net", nick="buildbot",
                              channels=["#myapp-test"])
c['status'] = [htmlStatus,ircStatus]

c['status'] = [htmlStatus,ircStatus]

# from buildbot.status import mail
# c['status'].append(mail.MailNotifier(fromaddr="buildbot@localhost",
#                                      extraRecipients=["builds@example.com"],
#                                      sendToInterestedUsers=False))
#
# from buildbot.status import words
# c['status'].append(words.IRC(host="irc.example.com", nick="bb",
#                              channels=["#example"]))
#
# from buildbot.status import client
# c['status'].append(client.PBListener(9988))


####### DEBUGGING OPTIONS

# if you set 'debugPassword', then you can connect to the buildmaster with
# the diagnostic tool in contrib/debugclient.py . From this tool, you can
# manually force builds and inject changes, which may be useful for testing
# your buildmaster without actually commiting changes to your repository (or
# before you have a functioning 'sources' set up). The debug tool uses the
# same port number as the slaves do: 'slavePortnum'.

#c['debugPassword'] = "debugpassword"

# if you set 'manhole', you can ssh into the buildmaster and get an
# interactive python shell, which may be useful for debugging buildbot
# internals. It is probably only useful for buildbot developers. You can also
# use an authorized_keys file, or plain telnet.
#from buildbot import manhole
#c['manhole'] = manhole.PasswordManhole("tcp:9999:interface=127.0.0.1",
#                                       "admin", "password")


####### PROJECT IDENTITY

# the 'projectName' string will be used to describe the project that this
# buildbot is working on. For example, it is used as the title of the
# waterfall HTML page. The 'projectURL' string will be used to provide a link
# from buildbot HTML pages to your project's home page.

c['projectName'] = "MyApp"
c['projectURL'] = "http://192.168.0.30/"

# the 'buildbotURL' string should point to the location where the buildbot's
# internal web server (usually the html.Waterfall page) is visible. This
# typically uses the port number set in the Waterfall 'status' entry, but
# with an externally-visible host name which the buildbot cannot figure out
# without some help.

c['buildbotURL'] = "http://192.168.0.30:8010/"

スレーブ用のアカウント作成

アカウント名はbuildbotで。

スレーブの作成

スレーブのアカウントで、スレーブ作成

buildbot create-slave /home/buildbot/Buildbot/slave/MyApp 192.168.0.30:9989 MyApp-bot buildbot

スレーブの設定ファイルは、buildbot.tacに記述されている。

マスターとスレーブの起動

master% buildbot start /home/buildbot/Buildbot/slave/MyApp
buildbot % buildbot start /home/buildbot/Buildbot/master/MyApp

これで、上記のmaster.cfgの設定では、Subversionにチェックインされたコードがあればビルドされます。また、Dailyでビルドされます。結果は、http://192.168.0.30:8010/で見ることができます。

テストが失敗していれば、その部分が「赤く」表示されます。

Catalystアプリのテストと書いていますが、make testしているだけなので、他のものでもテストできます。

設定・実行の際の注意点

  • 記述が間違っている場合のエラーメッセージが貧弱なので、エラーがでたらStackTraceを見て、どこが間違っているかを探す。
  • twistd.* は消してから、buildbot start を実行しないとダメ
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080308

2008-03-07

CatalystでのMVCの私的まとめ

20:40 |  CatalystでのMVCの私的まとめ - dann@catalyst を含むブックマーク はてなブックマーク -  CatalystでのMVCの私的まとめ - dann@catalyst  CatalystでのMVCの私的まとめ - dann@catalyst のブックマークコメント

id:miyagawaさんやid:charsbarさんのお陰もあって、自分の中では一通り考えがまとまったので、まとめてみることにしました。

僕の場合、Catalystでテスタビリティが高いWebアプリを作るためにはどうすればよいのかとここ数週間考えていて、それを実現するにはどうすればいいのかという点に興味がありました。Catalyst::Model::Adaptor使っても、うまくいかないなぁと思っていたので、なんかいい方法ないのかなと思ってました。

特に、DBICSchemaにロジックを置くということに何か問題があるか、またテスタビリティ上問題がないのかという点がよくわからなかったので疑問点を書いたわけでした。

最終的には、http://catalyst.g.hatena.ne.jp/dann/20080305/1204732092 で書いた構成と殆ど変わらない形でも、テスタビリティは維持できそうだという結論に至りました。

Model

  • CatalystのModel」は使わない
  • テーブルのデータに固有のロジックは、DBのモデル(DBICSchema)にロジックを記述する
    • データに関連のあるオブジェクトがデータに対する責務を担う
    • 前提条件は、DBのモデルのStubが簡単に作れ、DBを介さずにユニットテストができること
  • 複数のDBのモデルにまたがるロジックは、Serviceクラス(Cというクラス)に記述する
  • Serviceクラスは、Catalystのモデルには依存させない
    • Catalystでの依存を断ち切ることで、フレームワークに依存せず、ユニットテストができるようにすること
    • フレームワークに依存していなければ、CLIなどでも簡単に使えるようにもなるということ。これはプロダクトの性質だけの問題で必要がない人もいるでしょうが、メンテナンス用の管理コマンドとして、プロダクトの場合には特にCLIのツールは必要かなという気がします。

Controller

  • Controllerにはロジックは記述させず、ServiceクラスまたはDBのモデルに処理を委譲するだけ
  • Webのコンテキストに依存する部分のみをControllerに残す
  • Controllerの主な役割は、URLマッピングとViewへのフォワードだけ

View

  • Catalyst依存
    • Viewだけの単体テストをする必要はないから

まだ、以下のエントリで書いた疑問点は完全に解決はしていませんが、大体どんな形になりそうかは想像がつきます。

http://catalyst.g.hatena.ne.jp/dann/20080307/1204847350

# miyagawaさんからのブクマコメントで、Catalyst界隈では、以下のBread BoardがDIコンテナとして注目されているとのことでした。

http://search.cpan.org/~stevan/Bread-Board-0.03/

銀の弾MVCアーキテクチャ

20:40 |  銀の弾MVCアーキテクチャ - dann@catalyst を含むブックマーク はてなブックマーク -  銀の弾MVCアーキテクチャ - dann@catalyst  銀の弾MVCアーキテクチャ - dann@catalyst のブックマークコメント

どんな要求をも満たすMVCアーキテクチャというのはないわけで、全てトレードオフがあると思うんですね。それで、フレームワークは、全ての要求を満たすものである必要も当然ないと思っています。

アーキテクチャの議論でありがちなアンチパターンは、何の前提条件もなく、このアーキテクチャは良い、悪いという議論をしてしまうことかなと思っています。ですから、どんな条件のときに、どんなアーキテクチャであればマッチするのかというのを抜きに、アーキテクチャの議論をしてみても、あまり意味がないかなと思っています。

ORマッパーの変更がVCにも影響がでないように設計したいという状況があれば、DBICSchemaにロジックを置くということは当然できないわけです。ただ、そうした要求が無ければDBICSchemaにロジックを置いても問題がないわけです。

他にも、LDAPDB、FileなどのStorageの抽象化が必要な場面で抽象化するというのも、抽象化が必要なんであればそういうデータモデルの抽象化をするというのは必要なんだと思います。ただ、Storageが切り替わる必要がなければ、その抽象化をする必要がないわけです。

システムに必要とされる前提条件があるもとでのアーキテクチャの良し悪しについては興味があって、こうしたほうがいいよ、こんなときにはダメだよっていう話は、いろいろとしてみたいなぁって思ってます。

Serviceクラス(Cというクラス)のCatalystのModelからの依存を断ち切るには?

08:49 |  Serviceクラス(Cというクラス)のCatalystのModelからの依存を断ち切るには? - dann@catalyst を含むブックマーク はてなブックマーク -  Serviceクラス(Cというクラス)のCatalystのModelからの依存を断ち切るには? - dann@catalyst  Serviceクラス(Cというクラス)のCatalystのModelからの依存を断ち切るには? - dann@catalyst のブックマークコメント

SpringなどのDIコンテナでは、DAOがどのDBを見に行くかとかというのは、DIコンテナがDAOを生成するときに設定する。それは、Webなどのコンテキストには依存していないし、Webのフレームワークには依存していない。

今は、CatalystのConfigLoaderでmyapp.ymlを読み込んで、Modelはその情報を参照している。要するに、CatalystがDIコンテナの一部の役割を担っているということになる。Webのコンテキストという意味では依存していないのだけれど、Catalystには依存している。

ここを切り離さないと、Cというクラス(自分はServiceと呼んでいる)の、Catalystへの依存というのが切れない。依存を切り離す一つの解は、Catalyst::Model::Adaptorを使うことなのだけれど、Configurationを参照させる部分、または、DIコンテナを作るというほうが自然なのかもしれないと思った。

仮にCatalyst::Model::Adaptorを使って、Serviceに処理を委譲する形にしても、Serviceのバックエンドで動作するDBICSchemaは、どのDBにConfigLoaderを見にいくのかというのを知りたいわけだし、そうするとCatalystからの依存はCatalyst::Model::Adaptorを使っただけでは依存が切れないような気がした。

これはどう解決するのが綺麗なんだろうなぁと思っていたのだけれど、miyagawaさんのエントリを読んでから考え直した。

ControllerでのURLマッピング、Viewへのフォワード部分だけを、Catalystの機能として使って、後の部分はPOPO(Plain Old Perl Object)として扱って、DIコンテナで間を繋ぐという形が綺麗なのかもしれない。DIコンテナで繋がなくても、ConfigurationのデータをPOPOが参照できる仕組みがあれば、「Catalystに依存したCatalystのModel」の部分はなくすことができる気がする。

CatalystのModelでyaml参照させるためだけに、Catalystに依存してしまうということ自体が間違いかもしれないということが分かったのは、大きな収穫な気がするなぁ。

大分すっきりしてきた気はするので、あともう少し詰めれば、Catalystを使って、テスタビリティの高いアプリケーションも作れるような気がするなぁ。別の解もある気がするから、もう少し考えよう。

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080307

2008-03-06

Catalyst と MVC

01:51 |  Catalyst と MVC - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst と MVC - dann@catalyst  Catalyst と MVC - dann@catalyst のブックマークコメント

charsbarさんのお陰で、miyagawaさんがいっていることは、自分で納得できるところまでは理解できた気がしたので、まとめておきたい気もするんだけれど、今日はもう疲れたのでここまで。

Catalystのグループ用のメモとして、id:miyagawaさんのエントリにだけリンクははっておこう。結局、殆どmiyagawaさんのエントリに解が書いてあったということなので。

http://subtech.g.hatena.ne.jp/miyagawa/20080306/1204761778

テーブル:モデルが1:1になることの問題点はなんだろう?

01:51 |  テーブル:モデルが1:1になることの問題点はなんだろう? - dann@catalyst を含むブックマーク はてなブックマーク -  テーブル:モデルが1:1になることの問題点はなんだろう? - dann@catalyst  テーブル:モデルが1:1になることの問題点はなんだろう? - dann@catalyst のブックマークコメント

id:Baysideさんの話

http://d.hatena.ne.jp/Bayside/20080306/p1

以下の点は理解できます。

  • 複数のロジックを束ねるのにL(miyagawaさんがいっているCに近いもの)を置く
  • Controllerにロジックを書くのはよくない

ただ、以下の点はよくわからないです。

  • Catalyst 用語でいう stash と session は MyApp::Logic 内で隠蔽化してしまうと幸せになれます。
  • テーブル:モデル=1:1 じゃダメ

sessionがLogic内で隠蔽されたら、LogicがWebのコンテキストに依存してしまうので、直感的には良くない気がします。また、テーブル:モデルが1対1だとダメな理由というのがよくわからないです。

DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - その2

01:15 |  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - その2 - dann@catalyst を含むブックマーク はてなブックマーク -  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - その2 - dann@catalyst  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - その2 - dann@catalyst のブックマークコメント

DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか?」の記事のcharsbarさんのコメントを読んでみて、そんなに自分が考えていた仮定もそんなに間違いではないかもしれないなと思いました。

以下、id:charsbarさんのコメントです。

モデルそのものに押し込むのは正しいけど、Catalystのモデルに押し込んでしまうとCLIなどで使い回すときに不便だから避けた方が無難ということになってきている、ということかと

DBICのモデルにロジックを書くのは間違いではないけれど、「Catalystのモデル」にロジックを書いてしまうと、「Catalystのモデル」はフレームワークに依存してしまうから、CLIなどなどで使いまわすときに不便なので避けたほうがよい

という話なんだと理解しました。これで大分すっきりしました。DBICのモデルという書き方だと少し誤解を招くかもしれませんが、多分理解は間違ってないんじゃないかなぁと。id:miyagawaさんが書かれている話も、Modelを「Catalystのモデル」と置き換えてよむと、すんなり読めます。

Model に直接メソッドを書いて Controller から呼び出すのも別に悪くはないとおもうけどイベント処理とかいろんなところからメソッド呼んだりするので共通化できるメリットがあるし、 Web コンテキスト関係なくテストできるのでユニットテストも書きやすくなる。

これで、細かい点でどう実装するのか検討する点はありますが、大体どう作っていったほうがよいのかというのは分かった気がします。たった1日で疑問に思っていたことの大部分が解決してしまった!Blogって素晴らしいなぁ。

こういうときには機能しないよっていう話があったら、それも是非聞いてみたいですね。そういう話はとても貴重だと思うので、色んな人の話を聞いてみたいなと。

# これって、Virtual CatalystCon!

# そもそも、解釈がぜんぜんちげーってことだったら、是非教えてください。

フレームワークとの密結合していてよいのは?

22:42 |  フレームワークとの密結合していてよいのは? - dann@catalyst を含むブックマーク はてなブックマーク -  フレームワークとの密結合していてよいのは? - dann@catalyst  フレームワークとの密結合していてよいのは? - dann@catalyst のブックマークコメント

モデルがフレームワークに完全依存していたとしたら、それが問題なんじゃないかなぁというのは思っています。個人的には、ViewとControllerについては、フレームワークに依存していてもよいと思っています。

Controllerについては、Controllerがフレームワークに密結合していてもよいと思っています。というよりも、Controllerがフレームワークに密結合していないWebのフレームワークって一体なにをするんだ?っていう気すらします。Controllerには直接ビジネスロジックは書いてはいけないとは思っています。

CatalystのViewでCatalystのContextオブジェクトに依存する形で(例えばuri_forとか)、僕はコードを書いてますが、それは別に気になりません。Viewのファイルだけを単体でテストすることがないからです。

ただ、ビジネスロジックがフレームワークに依存するというのは、テスタビリティ上の問題があると思っていて、これは言語によらず問題なのではないかと思っています。

ただ、DBICのモデルにロジック書くのは、フレームワークには依存してないと思うんですよね。

DBにしか入れないデータでも抽象化は必要か?

22:22 |  DBにしか入れないデータでも抽象化は必要か?  - dann@catalyst を含むブックマーク はてなブックマーク -  DBにしか入れないデータでも抽象化は必要か?  - dann@catalyst  DBにしか入れないデータでも抽象化は必要か?  - dann@catalyst のブックマークコメント

id:yappoさんの日記に空前のMVCブームにのった記事のリンクが張られています。

http://d.hatena.ne.jp/yappo/20080306/1204781864

一通り全部のBlogを読んでみても、やっぱりまだしっくりとこないところが結構あるなぁということで、もう少し深く議論をしたい!

読んだ記事の中でも、id:miyagawaさんの記事は、納得できる話が多かったです。

納得しているのは、

  • LDAPDBなど異なるストレージに対する操作を抽象化するときに、抽象化したモデルが必要であるということ
  • 複数の M を操作するプロシージャコールみたいなメソッドをまとめて定義したクラスを用意する

という点です。

一つ目の話は、dmakiさんも書いていることです。これは自分もそう思います。これは、これしか解がないように思います。dmakiさんのBlogの例やmiyagawaさんのLDAPの例については、ストレージレベルでの抽象化が必要なデータだから、抽象化したデータモデルが必要なんだと思います。ここまでの話は特に違和感も何もなくて、自然な話だと思う。特に疑問に思うところがないです。

2つ目の話は、複数のモデル(DB)を操作するクラスが必要という点については、自分もそうなんじゃないかなぁと思ってます。ただ、一つのMだけに必要なロジックも、C というクラスに置いたほうがよいのか?というのは、わからないです。これについての疑問は、次のエントリに書きます。

まずは、話が長くなるので、一つ目の話だけをします。

  • DBにしか入れないデータがあったとしても、常に抽象化する必要があるのか?

1番目については、最近はNoだと思っています。将来的に抽象化が必要なことが予想されるのであれば、抽象化が必要なものなんだと思ってます。

DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか?

22:26 |  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - dann@catalyst を含むブックマーク はてなブックマーク -  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - dann@catalyst  DBにしか入れないデータでも、DBICのモデルにロジックを置くのは悪なのか? - dann@catalyst のブックマークコメント

yappoさんのBlogからのリンクの記事を読んでみても、自分が疑問に思っていることは、やっぱり解決していないなぁと思っています。

自分が疑問に思っているのは、

DBにしか入れないデータで、複数のモデルにまたがらないロジックでも、DBICSchemaにロジックを置くのは悪なのか?」

ということでです。

僕は、データを知っている人だけが、そのデータに対する振る舞いを定義するべきなんじゃないかと思うわけです。

その点、複数のモデル(データ)にまたがるロジックは、一つのDBICのモデルに押し込むのは変で、Serviceクラス(宮川さんが書いているCクラスに書くのは自然なことなんだと思っています。

こうしたときに、DBICのモデル(テーブルに対応)に固有のロジックは、DBICのモデルに直に書くのがいいんじゃないかと思っていて、何でそこに書いていけないのか?と思うのです。これに対する解は、どの記事にも書いていないように思いました。(自分の理解が正しければ、RailsのAcriveRecordは、まさにDBICのモデルに直にロジックを書く形なんじゃないかと思っています)

これに対する予想される解は、以下のことが考えられます。

  • DBICという一つのORマッパに依存してしまうこと
  • モデルがDBに依存することで、テスタビリティが下がること

一つ目の解である、「ORマッパへの依存」については、自分は問題ないと思っているというのは前の記事で書きました。開発後にORマッパを以降することがないからです。また、ORマッパの差異を吸収するラッパを書いて、それを使うというのは、現実的ではないと思っているからです。

二つ目の解である、テスタビリティが下がることについては、解があるんじゃないかという記事を書きました。Rails界隈のBlogをざっとみた限りでは、以下の記事は自分が思っている解に近いものだと思いました。簡単にまとめると、ActiveRecordの一部メソッドをStubでつくり、カラムのデータをテスト用に作るというものです。

http://blog.jayfields.com/2006/12/rails-activerecord-unit-testing.html

要するにORマッパレベルでStubを作ることで、DBへの依存を断ち切るわけです。多分、DBICでも同じことができるんじゃないかと思っています。簡単にStubが作れれば、テスタビリティが下がるという問題もなくなってしまいます。

DBなしでもUnitTestができるわけで、これも問題になりません。UnitTestレベルでは、DBに依存しないテストが簡単に作れそうです。

そう考えると、やっぱりどうしても、DBICのモデルにロジックを記述することの問題点というのがわからないのです。何が問題になるのだろう。これは、ActiveRecordのモデルにロジックを書くのは、何が問題なのかっていうことの問題と同じなんじゃないかと殆ど同じだと思っています。

このDBICのモデルにロジックを置く事に、何か問題(開発効率上、テスタビリティなどなど)があるのであれば本当に教えてもらいたいなぁって思う。

これで問題があれば、結局のところJava界隈で話されていたアーキテクチャに戻るということも当然あるとは思っているんですが、今のところこれといった問題がないように思えてしまうんですよね。Javaでは、DAOを切り離すしか解がなかったと思うので、アーキテクチャとして、ORマッパのモデルにビジネスロジックを置くという解がそもそもなかったと思います。だから、前のエントリで書いた、レイヤを切り離すアーキテクチャは正しかったんじゃないかと思います。ただ、Dynamic Languageでのアーキテクチャには、別の解があるんじゃないかと思っていて、それこそが自分の疑問点の始まりだったりします。

# DBICのモデルにロジックを置くのは間違いなのか?というのは、ActiveRecordのモデルにロジックを置くのは間違いなのか?と読み替えてもらってもよいのではないかと思います。

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080306

2008-03-05

DBを直接モデルとして扱うのは悪か?

00:48 |  DBを直接モデルとして扱うのは悪か? - dann@catalyst を含むブックマーク はてなブックマーク -  DBを直接モデルとして扱うのは悪か? - dann@catalyst  DBを直接モデルとして扱うのは悪か? - dann@catalyst のブックマークコメント

DBICをControllerから直接呼ぶのは本当に悪なのか?、これは一概には言えないんじゃないかと思っているのだけれど、Perl界隈でもそうなんだろうか。以下のようにレイヤ分割して、レイヤ間を疎に保つという話は、Javaの界隈では割によくやられていた話だ。大体以下のような構成だ。

Action -> Service-> Domain Model -> DAO -> OR Mapper

レイヤ間をDIコンテナで繋いで、各レイヤ間の依存関係は最小にして、テスタビリティを高めるというのが定石だった。ドメインモデルがDBに直接依存しないようになっていて、DAOの切り替えができるようになっているわけだ。さらに、Service、Domain Modelは、WebのフレームワークやORマッパなどに依存していない。

ただ、こういう構成は、各レイヤを繋ぐためのレイヤを作るコスト、レイヤを繋ぐデータのオブジェクトを作るコストがある。DBのレイヤが変わると、DTOが各レイヤに波及して、上位レイヤでも作り直しが発生する。Domain ModelのロジックはDBによらずにテストできるようになったが、疎に結合するようにするために数多くのレイヤを用意しなければいけなくなってしまった。

Domain Driven Designが2、3年前に流行った当時は、確かにこういう構成もありなんじゃないかと思っていた。けれど、実際にやってみると、ERモデルとオブジェクトモデルがあわないこともあるし、上記で書いたようにレイヤを用意するコストとレイヤ間を繋ぐコストが高くなってしまうこともある。

上記のような疎にするために余計なレイヤを挟むアーキテクチャよりも、ERモデルを直にモデルにするのでも良いのではないかと思うことが最近は多い。上記のような構成は、レイヤ間の疎結合を実現するために、無駄なコストを払いすぎているんじゃないか思うのだ。

Railsがでてきたときに衝撃的だったことのひとつは、Domain Model, DAO, DBIC部分はひとつにまとめて、ERモデルをそのままモデルとして扱ってしまうというアプローチだった。モデルが簡単に生成できて、DBを直にあらわしたモデルが簡単にテストできる仕組みさがあれば、密にモデルがDBに結合していても機能するんだということに驚いた。

それで結構十分だと思えるケースが結構多いことがあるんじゃないかと思う。だから、それだけRailsが受け入れられたんじゃないかと思う。

ここで言いたいのは、DBのモデルであるERモデルを、そのままモデルとして扱うのは絶対悪とはいいきれないんじゃなかってことだ。ORマッパを置き換えることなんて開発中にはまずないし、ORマッパ依存があったとしても、それは大きな問題じゃない。

モデルはDBに完全に依存していて、かつ各レイヤ間は密に結合していても、そこには無駄なものというのがひとつもなければそのほうがいいんじゃないかって思うのだ。

密結合の問題は、モデルがDBに依存してしまい、モデルのロジックのテストがDBなしでないとやりにくくなってしまうことだけだ。これはテスタビリティが下がり、プロダクト規模が大きくなればうまくいかなくなることがあるんじゃないかと思うこともある。

ただ、ここにはRubyPerlのようなDynamic Languageには解があると思う。テスト実行時にはクラス定義を上書きできるから。これがDIコンテナがRubyPerlでそこまで必要とされないひとつの理由なんだと思う。自分が考える、Perlのフレームワークの理想は、Railsが描くものにきわめて近い。疎にするための無駄なレイヤは作らないというのを基本スタンスをとるということ。

レイヤを疎に保つようににすれば、間に余計なレイヤが存在するようになるし、開発環境のサポートのない環境では、余分なレイヤはテストとコードの二重の変更が重荷になってくる。だから、Javaですら大変だったことは、同じようにPerlRubyでやればさらに大変になることは想像がつく。

だからこそ、簡単にDBICのモデルを直にモデルとして扱うことが悪になるとはいいきれないんじゃないかと思うのだ。

という話をCatalystConで話し合ってみたい!

Railsを触るようになってからドラスティックに考え方が変わったんだけれど、逆にこの密結合モデルは、テスタビリティ上、解決できない問題があるのかっていうのをもっと考えたい。疎に保つアプローチは、時にオーバースペックになりがちってことが、SpringのようなDIコンテナを使っていたときに思ったことの一つで、ピタリとはまるケースもあるんだけれど、そうじゃないケースも結構あるってこと。

自分が使うときのレイヤ分け

00:48 |  自分が使うときのレイヤ分け - dann@catalyst を含むブックマーク はてなブックマーク -  自分が使うときのレイヤ分け - dann@catalyst  自分が使うときのレイヤ分け - dann@catalyst のブックマークコメント

以下くらいの形で成り立つんじゃないかと最近思ってる。Javaerなときは、DIコンテナで繋ぐ形しかありえないと思っていたのだけれど、Railsを触ってからは大分変わった。

DBICのモデルに固有のロジックは、DBIC部分に。DBICのモデルをまたがるロジックは、Service部分にという形に今はしている。DBICのモデルは、Controllerに見せる形になっている。

Controller -> Service -> DBIC

Controller -> DBIC

Catalystを使ったJava風なアーキテクチャ

00:48 |  Catalystを使ったJava風なアーキテクチャ - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystを使ったJava風なアーキテクチャ - dann@catalyst  Catalystを使ったJava風なアーキテクチャ - dann@catalyst のブックマークコメント

Controller -> Service -> Domain Model -> DBIC

こういう形にすることのメリットは、

  • ServiceやDomain ModelがCatalystのフレームワークの依存から解き放たれること。
  • ControllerのロジックはServiceやDomain Modelに移り、Controllerの見にくいテストが減ること
  • ServiceやDomain ModelはPOJOならぬPOPOになるため、テスタビリティが高まること

デメリットは、

  • 疎に結合するために余分なレイヤを挟むことによるコストの増加。これはコード、テスト、DBレイヤ変更時の上位レイヤへの変更コストを含む。

こういう構成でやるときには、モデルのロジックがとても複雑な場合、モデルが多数のモデルに依存している場合なんじゃないかなって思う。

この構成でやるほど複雑な場合には、DIコンテナを使いたいなぁと。依存関係の解決はセッターインジェクションで解決されていて、ビジネスモデル側は具体的に何がインジェクションされるのかを知らない形にしたいから。具体的には、以下のような形にしたい。storageへの依存は、createのパラメータには渡さず、何に依存しているかはDIコンテナ側で事前に解決させたいなって思う。

MyApp::Model::BlogEntry->create({ 
    blog => $blog
    title => $entry_title
    content => $entry_content
});

Catalystを使っていて困ること

00:48 |  Catalystを使っていて困ること - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystを使っていて困ること - dann@catalyst  Catalystを使っていて困ること - dann@catalyst のブックマークコメント

  • DBICのモデルを直に扱うことによるテスタビリティの低下
    • ServiceやDBICのモデルのロジックがDBに依存してしまう
  • Serviceレイヤを設けないと、namespaceの問題で再利用性がゼロ
  • Controllerのテストをしようとすると、mockのリクエストを作ってテストが見難くなること

一つ目の問題は、Railsライクにテスティングフレームワーク側でDBをテストしやすい仕組みを用意するか、DBIC側のMockを簡単に作れる仕組みを用意するか、DBICのモデルをテスト実行時に上書きするかの方法で解決することになるんじゃないかと思う。ここは何がいいのかは迷っているけれど、なにかいい解はある気がするんだなぁ。Rails界隈でどうやってテストやってるのか、今度聞きにいってみようかなぁ。

2番目の問題は、Catalyst::Model::Adaptorで、ある程度解決。

3番目の問題は、極力Controllerにロジックを書かないようにすることで、ほぼ解決。

1番目の問題を綺麗に解決できる方法があれば、殆ど問題はないんだけどなぁ。

2008-03-04

Catalystのディレクトリ構成

00:11 |  Catalystのディレクトリ構成 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystのディレクトリ構成 - dann@catalyst  Catalystのディレクトリ構成 - dann@catalyst のブックマークコメント

現在のcatstarterで生成したディレクトリ構成は、下のような感じ。

  • confにdevelopment用、test用config生成。Railsからのパクり。
  • shareにdeployment関連の設定ファイルなど(ここの名前がなんかしっくりこない)
  • schemaディレクトリは、typesterさんのアイデアのまま

まだ、決定しきれてないのが、Adaptorで委譲するクラスの置き場。

追加してもいいだろうなぁと思っているのは、Controller/Auth.pm。大体、ほとんど同じやつ使っているような気がする。

API用のControllerは、もうちょっと考えようかなぁと、Catalyst::Controller::Resourcesを見て考え中。lib/MyApp/Controller/API/Hoge.pm のような形にしようかなぁと。

後は、割とよくみるCatalystのディレクトリ構成かな。root配下の構成が若干違うかもしれないけれど、そんな大きな差はないかも。基本、TTSiteベース。

大分、固まってきたかなぁというところ。

|-- Capfile
|-- Changes
|-- Makefile.PL
|-- README
|-- conf
|   |-- myapp.yml
|   |-- myapp_development.yml
|   |-- myapp_test.yml
|   `-- profiles.yml
|-- lib
|   |-- MyApp
|   |   |-- Controller
|   |   |   |-- API.pm
|   |   |   `-- Root.pm
|   |   |-- Date.pm
|   |   |-- I18N
|   |   |-- Model
|   |   |   `-- DBIC.pm
|   |   |-- Schema.pm
|   |   `-- View
|   |       |-- JSON.pm
|   |       `-- TT.pm
|   `-- MyApp.pm
|-- logs
|   |-- apache_app
|   `-- apache_web
|-- root
|   |-- static
|   |   |-- css
|   |   |   |-- common
|   |   |   |   |-- common.css
|   |   |   |   |-- default.css
|   |   |   |   `-- layout.css
|   |   |   `-- site.css
|   |   |-- images
|   |   `-- js
|   |       `-- site.js
|   `-- templates
|       |-- common
|       |   |-- config
|       |   `-- site
|       |       |-- footer.tt2
|       |       |-- header.tt2
|       |       |-- html.tt2
|       |       |-- layout.tt2
|       |       `-- wrapper.tt2
|       |-- email
|       |-- errors
|       `-- index.tt2
|-- schema
|   `-- lib
|       `-- MyApp
|           `-- Schema
|-- script
|   |-- myapp_cgi.pl
|   |-- myapp_create.pl
|   |-- myapp_fastcgi.pl
|   |-- myapp_schema_dumper.pl
|   |-- myapp_server.pl
|   |-- myapp_test.pl
|   `-- myapp_update_po.sh
|-- share
|   |-- deploy.rb
|   |-- sql
|   |   `-- schema.sql
|   `-- templates
|       |-- apache_app.erb
|       |-- apache_web.erb
|       |-- mycnf.erb
|       `-- setup_database.sql.erb
|-- t
|   |-- 00-compile.t
|   |-- 01app.t
|   |-- 02pod.t
|   |-- 03podcoverage.t
|   |-- 98-pod.t
|   |-- 99-perlcritic.t
|   |-- 99-pod-coverage.t
|   |-- fixtures
|   |-- lib
|   |   `-- MyApp
|   |       `-- Test
|   |-- model_DBIC.t
|   |-- perlcriticrc
|   |-- view_JSON.t
|   `-- view_TT.t
`-- tmp
    |-- cache
    `-- session

configurationをetcの下にいれて、複数の設定ファイルに分けているのは分かりやすいなぁと思った。shareディレクトリの下のところで真似したい。

http://hibinokoto.jp/archives/2008/03/post-251.html

MyApp::Util

00:11 |  MyApp::Util - dann@catalyst を含むブックマーク はてなブックマーク -  MyApp::Util - dann@catalyst  MyApp::Util - dann@catalyst のブックマークコメント

開発段階でどのクラスに押し込んでいいかわからない場合に使うことがあるなぁ。JavaScriptとかでもそうすることがある。ただ、大体後半になってアイデアがでて別クラスに責務を見つけて移動することが多い気がする。

KailinKailin2012/10/30 18:26It was dark when I woke. This is a ray of sunsihne.

sdpokyrdsdpokyrd2012/10/31 00:16g5nuh5 <a href="http://woxhgknbmxzo.com/">woxhgknbmxzo</a>

iufhjpiufhjp2012/11/01 06:22CTXWS8 , [url=http://vaibdsyojkir.com/]vaibdsyojkir[/url], [link=http://vqswepykpdqp.com/]vqswepykpdqp[/link], http://gfaexfpafmql.com/

hijbtbpsuihijbtbpsui2012/11/02 01:46U5EWKO , [url=http://fvfmbwqobotf.com/]fvfmbwqobotf[/url], [link=http://zuxthudgkfqp.com/]zuxthudgkfqp[/link], http://pmxjiqjiyhbe.com/

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080304