Hatena::Groupcatalyst

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

2008-01-31

CapistranoでDeploymentに不足しているもの

23:06 |  CapistranoでDeploymentに不足しているもの - dann@catalyst を含むブックマーク はてなブックマーク -  CapistranoでDeploymentに不足しているもの - dann@catalyst  CapistranoでDeploymentに不足しているもの - dann@catalyst のブックマークコメント

  • StagingサーバーへのDeploy
  • 環境に応じたアプリケーションサーバーなどの設定ファイルの自動生成とDeploy
  • リリースしたコードへのタギング

上記の機能を追加すれば、実際に使えるようになるんじゃないかと。

CapistranoでCatalystアプリをDeploy

22:36 |  CapistranoでCatalystアプリをDeploy - dann@catalyst を含むブックマーク はてなブックマーク -  CapistranoでCatalystアプリをDeploy - dann@catalyst  CapistranoでCatalystアプリをDeploy - dann@catalyst のブックマークコメント

config/deploy.rbに以下のものを記述。

set :application, "MyApp"
set :repository,  "file:///var/svn/repos/#{application}/trunk"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/var/www/#{application}"

role :app, "192.168.0.30"
role :web, "192.168.0.30"
role :db,  "192.168.0.30", :primary => true

namespace :deploy do
  task :finalize_update, :except => { :no_release => true } do
    stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
    asset_paths = %w(images css js).map { |p|
      "#{latest_release}/root/static/#{p}"
    }.join(" ")
    run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
  end

  # TODO
  task :migrate, :roles => :db, :only => { :primary => true } do
  end

  task :start, :roles => :app do
    sudo "/etc/init.d/apache2 start"
  end

  task :stop, :roles => :app do
    sudo "/etc/init.d/apache2 stop"
  end


  task :restart do
    sudo "/etc/init.d/apache2 restart"
  end
end

Capfile

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
load 'config/deploy'

殆どの実装は、以下のファイルに入っているので、Railsに関連しないところだけをdeploy.rbで上書きすればよいだけ。

/var/lib/gems/1.8/gems/capistrano-2.1.0/lib/capistrano/recipes/deploy.rb

さほど変更いらなかったなぁ。

使い方

% cap deploy

TODO

  • Apacheの設定ファイルの自動生成
  • catastarterへの組み込み
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080131

2008-01-30

Capistranoのタスク

23:29 |  Capistranoのタスク - dann@catalyst を含むブックマーク はてなブックマーク -  Capistranoのタスク - dann@catalyst  Capistranoのタスク - dann@catalyst のブックマークコメント

deploy.rbの生成

% capify MyApp

[add] writing `MyApp/Capfile'

[add] writing `MyApp/config/deploy.rb'

[done] capified!

生成されたのは、こんなファイル。

set :application, "set your application name here"
set :repository,  "set your repository location here"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}"
# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion

role :app, "your app-server here"
role :web, "your web-server here"
role :db,  "your db-server here", :primary => true

タスク一覧

% cap -T

cap deploy # Deploys your project.

cap deploy:check # Test deployment dependencies.

cap deploy:cleanup # Clean up old releases.

cap deploy:cold # Deploys and starts a `cold' application.

cap deploy:migrate # Run the migrate rake task.

cap deploy:migrations # Deploy and run pending migrations.

cap deploy:pending # Displays the commits since your last deploy.

cap deploy:pending:diff # Displays the `diff' since your last deploy.

cap deploy:restart # Restarts your application.

cap deploy:rollback # Rolls back to a previous version and restarts.

cap deploy:rollback_code # Rolls back to the previously deployed version.

cap deploy:setup # Prepares one or more servers for deployment.

cap deploy:start # Start the application servers.

cap deploy:stop # Stop the application servers.

cap deploy:symlink # Updates the symlink to the most recently deployed ...

cap deploy:update # Copies your project and updates the symlink.

cap deploy:update_code # Copies your project to the remote servers.

cap deploy:upload # Copy files to the currently deployed version.

cap deploy:web:disable # Present a maintenance page to visitors.

cap deploy:web:enable # Makes the application web-accessible again.

cap invoke # Invoke a single command on the remote servers.

cap shell # Begin an interactive Capistrano session.

タスク名みた感じだと、symlink, restart, start, stopあたりは上書きしないとダメだろうな。

ディレクトリ構造もタスクも大体わかったから、後はcapistranoのコードを見て、上書きすればいいかな。

以下のファイルを読んでいけばよい。

/var/lib/gems/1.8/gems/capistrano-2.1.0/lib/capistrano/recipes/deploy.rb

Rails2.0とCatalystのディレクトリの対応

23:29 |  Rails2.0とCatalystのディレクトリの対応 - dann@catalyst を含むブックマーク はてなブックマーク -  Rails2.0とCatalystのディレクトリの対応 - dann@catalyst  Rails2.0とCatalystのディレクトリの対応 - dann@catalyst のブックマークコメント

  • libがapp
  • root/staticがpublic

に対応。

Rails2.0のディレクトリ構造

23:29 |  Rails2.0のディレクトリ構造 - dann@catalyst を含むブックマーク はてなブックマーク -  Rails2.0のディレクトリ構造 - dann@catalyst  Rails2.0のディレクトリ構造 - dann@catalyst のブックマークコメント

% rails MyApp

% tree -d MyApp

MyApp
|-- app
|   |-- controllers
|   |-- helpers
|   |-- models
|   `-- views
|       `-- layouts
|-- config
|   |-- environments
|   `-- initializers
|-- db
|-- doc
|-- lib
|   `-- tasks
|-- log
|-- public
|   |-- images
|   |-- javascripts
|   `-- stylesheets
|-- script
|   |-- performance
|   `-- process
|-- test
|   |-- fixtures
|   |-- functional
|   |-- integration
|   |-- mocks
|   |   |-- development
|   |   `-- test
|   `-- unit
|-- tmp
|   |-- cache
|   |-- pids
|   |-- sessions
|   `-- sockets
`-- vendor
    `-- plugins

Railsは、以下の方法でインストール。

% sudo gem install rails -y

Capistranoのインストール

23:05 |  Capistranoのインストール - dann@catalyst を含むブックマーク はてなブックマーク -  Capistranoのインストール - dann@catalyst  Capistranoのインストール - dann@catalyst のブックマークコメント

% sudo aptitude install ruby

% sudo aptitude install ruby1.8-dev

% sudo aptitude install rubygems

% sudo gem install capistrano -y

% sudo gem install fastthread

なんか2系になってから変わったみたいだなぁ。

CatalystConference

21:13 |  CatalystConference - dann@catalyst を含むブックマーク はてなブックマーク -  CatalystConference - dann@catalyst  CatalystConference - dann@catalyst のブックマークコメント

http://coderepos.org/share/wiki/CatalystCon1

ちょっ... 聞きたいことは山のようにあるんだけれど...

  • Catalystでのオレオレ規約
    • ディレクトリ構成やURI設計とか含めて、こんなんやってるよ!ってのを聞いてみたい!
  • REST時代でのCatalystでのURI設計
  • CatalystアプリのDeploy自動化
  • Catalystアプリのテストサーバー環境
    • mod_perl再起動とか開発遅すぎなので、catalyst付属のテストサーバーで開発やってるんですが、いざmod_perl上にDeployしたら動かない... orzなんてことになりがちなので、なんか上手い方法ないのかなっていう。
  • DBICとMemcachedの組み合わせ方(Catalystじゃないって?...)
    • Memcached使ってDBICのオブジェクトキャッシュしようとすると、DBを"R"DBっぽくは使えなくなるような気がするのだけれど、これはそういう使い方であってるのかなぁという...

いやぁ、リストアップしてみると、聞いてみたいことはたくさんあるなぁ。

mod_proxy_balancer + mod_perlでCatalyst@Ubuntu7.10

20:32 |  mod_proxy_balancer + mod_perlでCatalyst@Ubuntu7.10 - dann@catalyst を含むブックマーク はてなブックマーク -  mod_proxy_balancer + mod_perlでCatalyst@Ubuntu7.10 - dann@catalyst  mod_proxy_balancer + mod_perlでCatalyst@Ubuntu7.10 - dann@catalyst のブックマークコメント

SC440を購入したので、Catalystの環境を作ってみることにしました。


自宅にそんなにたくさんサーバー置けるスペースないよ!って話なんですが、無駄にスケールできるようにしてみました。具体的には、アプリケーションサーバーをスケールアウトできるように、フロントエンドとバックエンドを分ける構成にしてみました。

さて、以下がフロントエンドとバックエンドの設定の説明です。Catalystのディレクトリ構成にあうように設定しています。

フロントエンド

 フロントエンドのApacheは、Apache2.2を使って「ロードバランサ + 静的コンテンツのサーブ」という構成にすることにしました。

  • mod_proxy_balancerでアプリケーションサーバーにリクエストをディスパッチ
  • CSSJavaScriptなどのstaticなコンテンツは、このフロントエンドのサーバーでサーブ。Catalystではroot/staticに静的コンテンツが格納されているので、これらをサーブしています。

以下が設定したApacheのファイルです。

NameVirtualHost *:80
<VirtualHost *:80>

        <IfModule proxy_module>
            # フロントのproxyが詰まらないようにする
            # ために、timeout時間を短くする
            ProxyTimeout 3
        </IfModule>


        Include /etc/apache2/conf.d/rewrite.vhost_common
        ServerName SUBDOMAIN
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www/SUBDOMAIN/root
        Alias /static /var/www/SUBDOMAIN/root/static

        ProxyRequests Off
        <Proxy *>
            Order deny,allow
            allow from all
        </Proxy>

        # App server
        <Proxy balancer://SUBDOMAINCLUSTER/>
            BalancerMember http://192.168.0.30:8080 loadfactor=10 keepalive=On
        </Proxy>

        ProxyPass /static/images !
        ProxyPass /static/js !
        ProxyPass /static/css !
        ProxyPass /favicon.ico !
        ProxyPass / balancer://SUBDOMAIN_CLUSTER/
        ProxyPassReverse / balancer://SUBDOMAIN_CLUSTER/
        # バックエンドでVirtualHostの場合に
        ProxyPreserveHost On

       <Location /static>
            SetHandler  default-handler

            # mod_filterでdeflate
            <IfModule filter_module>
                FilterDeclare deflate CONTENT_SET
                FilterProvider deflate DEFLATE Content-Type /text\x2F\w+|application\x2Fx-javascript/
                FilterChain deflate
            </IfModule>
            # mod_expiresでexpire
            <IfModule mod_expires.c>
                ExpiresActive On
                ExpiresDefault "access plus 1 year"
                ExpiresByType image/gif "access plus 1 week"
                ExpiresByType image/jpeg "access plus 1 week"
                ExpiresByType image/png "access plus 1 week"
                ExpiresByType application/x-shockwave-flash "access plus 1 week"
                ExpiresByType text/html "access plus 1 week"
                ExpiresByType text/css "access plus 1 week"
                ExpiresByType application/x-javascript "access plus 1 week"
            </IfModule>
       </Location>

        ErrorLog /var/log/apache2/SUBDOMAIN/error.log
        LogLevel warn
        CustomLog /var/log/apache2/SUBDOMAIN/access.log combined
        ServerSignature On

</VirtualHost>

幾つか設定のポイントがありました。

  • Staticなコンテンツをサーブするだけなので、KeepAliveはOnに設定しておきます。
  • フロントエンドではStaticなコンテンツは、deflateで圧縮、expiresでヘッダの設定。
    • deflateで圧縮はCPU負荷を考えるとやめるべきなのかもしれません。gzip圧縮されたものを直に配信というほうがBetterなのかもしれません。
  • フロントエンドがつまらない様に、ProxyTimeoutの時間を短く

今後のTODOとしては、キャッシュなんじゃないかと思います。mod_mem_cacheでキャッシュすることになるのかなぁと思っています。まともに使ったことがないので、Catalystのキャッシュ機構とあわせて、後々検証してみることにします。

これで、balancerのメンバにアプリケーションサーバーを追加してやれば、スケールするようになるんじゃないかと思います。一応、アプリケーションサーバーを2台にしてみた感じでは振り分けられていたので、一応は動いているんじゃないかと。

YSlowの評価もA評価になったので、まぁこれ位でいいんじゃないかと思います。

バックエンド

 バックエンドはmod_perl2で構築することにします。プロセスサイズが大きくなるので、KeepAliveはOffにします。

NameVirtualHost 192.168.0.30:8080
<VirtualHost 192.168.0.30:8080>
        ServerName SUBDOMAIN
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www/SUBDOMAIN/root

        PerlModule MyApp
        PerlOptions +Parent
        PerlSwitches -I/var/www/SUBDOMAIN/lib
        <Location />
            SetHandler modperl
            PerlResponseHandler MyApp
        </Location>

        ErrorLog /var/log/apache-perl/SUBDOMAIN/error.log
        LogLevel warn
        CustomLog /var/log/apache-perl/SUBDOMAIN/access.log combined
        ServerSignature On
</VirtualHost>

バックエンドのサーバーの設定のポイントとしては、

  • KeepAliveはOff

でしょうか。

バックエンドのサーバーのログに、フロントエンドのサーバーのIPが記録されてしまうので、mod_rpafを使って、以下のように設定して、クライアントのIPアドレスをバックエンドのサーバーのログに記録するようにします。

具体的には、mod_rpafをインストールして、RPAFproxy_ipsに「ReverseProxyのローカルアドレス」を設定するだけです。バックエンドのサーバーで設定するということがポイントです。

<IfModule mod_rpaf.c>
    RPAFenable On
    RPAFsethostname On
    RPAFproxy_ips 192.168.0.30        #Reverse Proxy のローカルアドレス
</IfModule>

MaxClientsなどの設定は、1台構成なので、DBサーバーとの絡みもあるので、また後ほど検討してみます。本業はWebアプリ屋さんではないので、おかしなところもあるかもしれませんが、逆に捨てるものは何もないので全部公開していこうかと思っています。

Catalystの自動Deployment

20:32 |  Catalystの自動Deployment - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystの自動Deployment - dann@catalyst  Catalystの自動Deployment - dann@catalyst のブックマークコメント

上記構成にしてみたのですが、今は手動でDeployしているので、面倒すぎです... catastarterを使って、立ち上がりはかなり早くなったものの、Deployする段階でやる気がなくなってしまいます。

ということで、Deploymentの自動化の仕組みも考えたいなと思っています。CapistranoかArcherを使うことになるのかなぁと思ってます。Perlで統一したほうが気持ちいいよねということで、Archerのほうでチャレンジしてみようかと思っていたり。

Catalystでの再利用 その2

20:32 |  Catalystでの再利用 その2 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystでの再利用 その2 - dann@catalyst  Catalystでの再利用 その2 - dann@catalyst のブックマークコメント

typesterさんの記事を見て、再利用にも幾つか種類があるなと思った。

  • catastarterでの再利用
    • ディレクトリ構成
    • テンプレート
    • プラグインとその設定
  • Catalyst::Model::Adaptorでのモデルの再利用
  • Baser ControllerでのControllerの再利用
    • これは上手くいった試しがない...
  • ヘルパでの再利用

ヘルパでの再利用は、殆どやっていなかったのでなるほどなぁと思いました。ちょっと取り込んでみたいと思います。

Catalystでの再利用 その3

20:43 |  Catalystでの再利用 その3 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystでの再利用 その3 - dann@catalyst  Catalystでの再利用 その3 - dann@catalyst のブックマークコメント

Java界隈で、こういう責務に関するデザインの話は、割とよく出る話で実際にやるとなると、

  • MyApp::Logic::HogeLogicみたいなのを使って、Logic切り出し。LogicはStateless
  • DAOは直接触らないでDTOにして、LogicではDTOを触る。ViewでもDTOを触る。

なんて話になりがちなのだけれど、こういうのはCatalystには合わないなぁって思う。

なんか、綺麗な落としどころがあるんじゃないかと思っているけれど、まだすっきりしていない。多分、Webアプリ作っている量が足りないんだとは思う。実際に作っているWebアプリもそんなにロジックが複雑なわけでもないから、モデルにロジック押し込むだけで事足りてしまっているというのがある。

さて、何がいいんだろうなぁ。

JamoscingeJamoscinge2017/05/11 02:08Amoxicillin Blood Pressure Dangers Priligy Se Vende Sin Receta Amoxicillin Over The Counter Lavitra <a href=http://byuvaigranonile.com>viagra</a> Super Kamagra Ajanta Pharma

CarcownCarcown2018/02/10 11:14Cialis Sotto La Lingua Cout Levitra 20mg <a href=http://tadalafbuy.com>cialis</a> Purchase Prescription Antibiotic Amoxicillin 875

DenthesDenthes2018/04/23 12:08Suprax No Prescription Cialis Generika Kaufen Deutschland Stress Tea <a href=http://brandciali.com>online pharmacy</a> buy accutane ireland Comprar Propecia En Espana

MatSworryMatSworry2018/10/08 13:41Shigru Zithromax Patient Education Prix Cytotec Au Maroc <a href=http://brandciali.com>cialis vs viagra</a> Delivered Amoxicillin Acquistare Kamagra Thailandia Ciprofloxacin Et Alcool

WilliamidemaWilliamidema2019/02/02 11:25cialis online usa prescriptions
cost of cialis at walgreens pharmacy
<a href=https://greatwinesgrandhouses.com>Cheap cialis buy online</a>
cialis kaufen rezeptfrei
side effects for cialis
https://greatwinesgrandhouses.com
discount brandedcialis online
aarp discount cialis
<a href=https://greatwinesgrandhouses.com>buy Cialis 20 mg</a>
cialis super active cheap
cialis in germany
https://greatwinesgrandhouses.com

ArnoldwalArnoldwal2019/02/05 20:48buy cialis in singapore 166
buy cialis mastercard
<a href="http://cialistlm.com">Cheap Cialis</a>
buy cheap cialis pills
cialis samples for physicians
<a href="http://cialisfurr.com">buy cialis</a>
cialis for daily use cost at walmart
cialis tadalafilo
<a href="http://xcialisxx.com">Buy Cheap Cialis Online</a>

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

2008-01-29

Catalyst::Plugin::URI::MtimeQueryとCatalyst::Plugin::Assets

00:25 |  Catalyst::Plugin::URI::MtimeQueryとCatalyst::Plugin::Assets - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst::Plugin::URI::MtimeQueryとCatalyst::Plugin::Assets - dann@catalyst  Catalyst::Plugin::URI::MtimeQueryとCatalyst::Plugin::Assets - dann@catalyst のブックマークコメント

http://unknownplace.org/memo/2006/03/17#e005

Catalyst::Plugin::AssetsにCatalyst::Plugin::URI::MtimeQueryのアイデアをインスパイアしたものを加えると、Assetsも結構使えるかも知れないなぁ。export_with_mtimequeryみたいなのがあればいいのかも。

Catalystでの再利用

00:45 |  Catalystでの再利用 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystでの再利用 - dann@catalyst  Catalystでの再利用 - dann@catalyst のブックマークコメント

http://vkgtaro.jp/2008/01/27/000623

id:vkgtaro さんのCatalystのわかりやすい説明。途中で再利用が難しいって話がでている。これは難しいなぁと思っていて、ほかの人がどうしているのか知りたいなぁ。

今、ある程度再利用できているのが、SchemaCatalyst::Model::Adaptorから委譲したPlainなクラス。それ以外はほとんど捨てることになっている。Controllerは、命名規則の再利用程度しかできないのかなぁと。基本的にはControllerは捨てて、Modelにロジックを押し込んで、Catalyst::Model::Adaptorを使ってModelを再利用という形になるのかなぁという気がしている。

Controllerについては、一部Advent Calendarにアプリケーションデザインのテクニックが書いてあるんだけれど、正直Baseのコントローラを上手く作れたことがない... 何かアプリケーションで共通になるようなBaseのコントローラがあれば、Conrollerも一部再利用ができるのかもしれないとか思っているんだけれど、上手くいかないなぁと。Catalyst本にはここらも書いてあるんだろうか。

http://catalyst.perl.org/calendar/2007/3

ただ、一部再利用できそうかなぁって思っているのはRSS生成とか。今は殆どをコントローラに書いているのだけれど、何か再利用できそうなんだよなぁと。そもそもコントローラじゃなくて、モデルに押し込むべきなのかもしれないけれど。

ChampChamp2011/12/27 22:51That insight would have saved us a lot of efofrt early on.

cerfihvxzvocerfihvxzvo2011/12/28 18:39XMZCOb <a href="http://svjtioubrayg.com/">svjtioubrayg</a>

ttdmnheubkwttdmnheubkw2011/12/29 00:22DBnzrj , [url=http://qxpjirvzofmg.com/]qxpjirvzofmg[/url], [link=http://yotvakhavony.com/]yotvakhavony[/link], http://pyxjaemxpkfr.com/

bhflgtubbhflgtub2011/12/29 19:18XngLjd <a href="http://wljcusswhfun.com/">wljcusswhfun</a>

rqizogbrxrqizogbrx2011/12/29 23:03C4cs0U , [url=http://plzzggucakhn.com/]plzzggucakhn[/url], [link=http://bgccbjjqfxqh.com/]bgccbjjqfxqh[/link], http://elpmfyvrvbvu.com/

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

2008-01-22

Catalyst::Model::Adaptor

02:10 |  Catalyst::Model::Adaptor - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst::Model::Adaptor - dann@catalyst  Catalyst::Model::Adaptor - dann@catalyst のブックマークコメント

lib/Hoge/Model 以下にPlainなクラスを配備、Catalyst::Model::AdaptorでCatalystのModel作成という形に落ち着きました。すっきり。

Modelのテスト

02:10 |  Modelのテスト - dann@catalyst を含むブックマーク はてなブックマーク -  Modelのテスト - dann@catalyst  Modelのテスト - dann@catalyst のブックマークコメント

Railsのfixtureみたいなのがないと、モデルのテストが面倒だなぁ。ここらはみんな自作しているんだろうか。

Test::Fixture::CDBIをまねて、DBIC版でも作ればばいいのかな。

http://d.hatena.ne.jp/tokuhirom/20060505/1146798752

まだ、Catalystでのテスト方法がいまいちわかってないなぁ。Plainなクラスのほうは、Catalystの依存が切れたからテストはしやすいのだけれど。

Catalyst::Controller::Resources

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

http://catalyst.g.hatena.ne.jp/ikasam_a/20071220/1198166773

CatalystRESTAPIを作るときにとても綺麗にControllerが書ける。素晴らしいなぁ。

Catalyst::Plugin::Assets

02:10 |  Catalyst::Plugin::Assets - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst::Plugin::Assets - dann@catalyst  Catalyst::Plugin::Assets - dann@catalyst のブックマークコメント

http://search.cpan.org/~rkrimen/Catalyst-Plugin-Assets-0.012/lib/Catalyst/Plugin/Assets.pm

今までは、MakefileJavaScriptを一つのファイルにしてからminifyということををやっていたけれど、Catalystでやる場合、CatalystのPluginでやるのもいいかもしれない。YAMLのconfig一つで、combineとminifyを切り替えられるような仕組みを作っておくのは、便利かもしれないなと。

一つのファイルにcombineするわけではないようだけれど。

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

2008-01-09

自分のcatstarterをcodereposに移動

22:35 |  自分のcatstarterをcodereposに移動 - dann@catalyst を含むブックマーク はてなブックマーク -  自分のcatstarterをcodereposに移動 - dann@catalyst  自分のcatstarterをcodereposに移動 - dann@catalyst のブックマークコメント

yappoさんにアカウントを作って頂いたので、codereposに移動。

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

何点か改善したい点があるので、これからもちょくちょくと改善していきます。

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

2008-01-04

make_schema_at

04:17 |  make_schema_at - dann@catalyst を含むブックマーク はてなブックマーク -  make_schema_at - dann@catalyst  make_schema_at - dann@catalyst のブックマークコメント

追加するもの。

  • inflate/deflate
  • 関連

schema/lib/MyApp/Schema/*に追加。

後は自動生成。

HTML::FormFu->FormValidator::Simple::Auto

04:17 |  HTML::FormFu->FormValidator::Simple::Auto - dann@catalyst を含むブックマーク はてなブックマーク -  HTML::FormFu->FormValidator::Simple::Auto - dann@catalyst  HTML::FormFu->FormValidator::Simple::Auto - dann@catalyst のブックマークコメント

フォームのデザイン変えたいときにどうするのかがいまいちよく分からず、結局FormValidator::Simple + Autoに戻った。FormFuのTTのテンプレート変更かCSSでデザインすんのかな?

OpenID+Catalyst

04:33 |  OpenID+Catalyst - dann@catalyst を含むブックマーク はてなブックマーク -  OpenID+Catalyst - dann@catalyst  OpenID+Catalyst - dann@catalyst のブックマークコメント

Catalystの勉強も兼ねてOpenIDでログインする掲示板作ってみた。Net::OpenID::Consumerがあるから実装も簡単。さくらインターネットのほうにUPしてみたのだけれど、Cache::FastMmapが動作しなかったので、Cache::CacheFileを使うことに。CGIだと無茶苦茶遅い。どっか他のレンタルサーバーにするか、自宅でサーバー立てるかしないと駄目だなぁ。

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

2008-01-03

Catalystで認証

01:56 |  Catalystで認証 - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystで認証 - dann@catalyst  Catalystで認証 - dann@catalyst のブックマークコメント

http://catalyst.perl.org/calendar/2007/6

  • Auth Controller作成
  • Root Controllerのautoメソッド中でAuth Controllerのcheck_loginメソッドをcall

Catalyst::Model::Adapter

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

http://catalyst.perl.org/calendar/2007/24

ざっと読んでみました。任意のクラスをCatalystのモデルにというものらしい。Catalystへの依存関係を切れるというのはいいかもしれない。基本的にCatalystのモデルクラスに処理を書かずに、全てAdaptorで委譲するというのがいいのかも。これでModelがCatalystに依存しないので、テストはしやすくなる。

これみると、ますますMangoのディレクトリ構成に変更したくなってくるなぁ。今のディレクトリ構成だとCatalystべったりだからなぁ。

catastarter MyApp::Catalyst -new

みたいにして、Catalystを押し込めてしまって、MyApp直下にPlain Classを配備ってのが綺麗かな。で、そのPlain Classに対してAdaptorから委譲すると。

starter周りの更新

00:49 |  starter周りの更新 - dann@catalyst を含むブックマーク はてなブックマーク -  starter周りの更新 - dann@catalyst  starter周りの更新 - dann@catalyst のブックマークコメント

TT2のテンプレートとCSSなどをざっくり追加して、昨日のエントリを更新。View周りのテンプレートなどが一通りあると、開発が始めやすいなぁと。ただ、starterの中に全部埋め込むのはちょっとやりすぎだったかも。もう少し汎用的にできたら、Mangoみたいな形にしよっと。

Mango

18:41 |  Mango - dann@catalyst を含むブックマーク はてなブックマーク -  Mango - dann@catalyst  Mango - dann@catalyst のブックマークコメント

Mangoのディレクトリ構成は、参考になるなぁ。

http://svn.mangoframework.com/CPAN/Mango/trunk/

  • CatalystをMango::Catalyst以下に押し込めている
  • SchemaディレクトリはMango::Schema、要するにCatalyst
  • デフォルトのテンプレート群をshare 以下に置いている

Catalystはメタなフレームワークだから、その上にどんな形で自分流のカスタマイズができるように工夫していけるかってところがポイントで、その一つの例がMangoなんだなと感じた。Mangoっぽく、Catalystを利用してフレームワークを作って、アプリケーションではそのフレームワークをExtendして使っていくってのはいいかもしれない。

いくつかアプリを作ってstarterカスタマイズしていって、共通で使えそうなところがでてきたら、Mangoっぽくフレームワークとして切り離して、育てていくってのがいいかな。

HTML::FormFu

18:41 |  HTML::FormFu - dann@catalyst を含むブックマーク はてなブックマーク -  HTML::FormFu - dann@catalyst  HTML::FormFu - dann@catalyst のブックマークコメント

Day 20 - HTML::FormFu - Handles forms, so you don't have to

FormValidator::Simple::Autoがかなりいいんじゃないかと思っていたけれど、HTML::FormFuもよさそうだなぁ。いい点は、以下の2点

  • Form定義とValidationルール、Validationのメッセージを一箇所に書ける点
  • 定義がYAMLで書け、読みやすい

ただ、HTMLの要素はテンプレート中に書いていったほうがいいのかなぁと思うこともあり、FormValidator::Simple::Autoのほうがいいのかなと思うこともあり。もう少しやってみてから考えることに。

catstartもCatalyst::Controller::HTML::FormFuを使うように変更して、root/forms以下にform定義を配備するようにしてみた。

See also: http://search.cpan.org/~cfranks/Catalyst-Controller-HTML-FormFu-0.02000/lib/Catalyst/Controller/HTML/FormFu.pm

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

2008-01-02

Catalystのstarter script

04:51 |  Catalystのstarter script - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystのstarter script - dann@catalyst  Catalystのstarter script - dann@catalyst のブックマークコメント

http://coderepos.org/share/browser/lang/perl/misc/catstarter/typester-catstarter.pl

を見て、自分用に少し書き換えました。typestarさんのstarterは、簡単に必要なファイルが追加できるようになっている点が素晴らしいです。

変更点

typesterさんのに加えて、以下を追加。

テンプレート群をざっと追加してやることで、大分開発を開始しやすくなった気がします。

TODO

今後のTODOとしては、

  • 使うプラグインの見直し
  • ConfigLoaderをConfigLoader::Multiを使うようにする
  • css, template周りの見直し

あたりかなぁ。

ディレクトリ構成

├─lib
│  └─MyApp
│      ├─Controller
│      ├─I18N
│      ├─Model
│      ├─Schema
│      └─View
├─root
│  ├─forms
│  ├─static
│  │  ├─css
│  │  │  └─common
│  │  ├─images
│  │  └─js
│  └─templates
│      ├─common
│      │  ├─config
│      │  └─site
│      └─errors
├─schema
│  └─lib
│      └─MyApp
│          └─Schema
├─script
├─sql
├─t
└─tmp

使い方

新規プロジェクトの作成

% catastarter.pl MyApp -new

スキーマの更新
  • lib/schema/MyApp/Schema以下に関連や追加したいメソッドを追加したスキーマファイルを作成
  • sqlitedb作成後に、以下を実行。

これで、lib/MyApp/Schema以下にスキーマファイルが生成される。

% ./script/myapp_schema_dumper.pl dbi:SQLite:/path/to/sqlite.db

関連や追加のメソッドをスキーマのほうに残して、残りはDBから生成というのは楽でいいなぁ。cool!

スクリプト

#!/usr/bin/env perl

use strict;
use warnings;

use Pod::Usage;
use Getopt::Long;
use Path::Class qw/file dir/;
use Template;
use YAML;

my @render_options = qw/json db email cache authentication i18n templates css perllib helper/;
GetOptions(
    \my %opt,
    qw/help new/, @render_options,
);
pod2usage(0) if $opt{help};

require Catalyst;
require Catalyst::Utils;

# dependency check
require Catalyst::View::TT::ForceUTF8;
require Catalyst::View::JSON;
require JSON::XS;
require Catalyst::Plugin::FillInForm::ForceUTF8;
require Catalyst::Plugin::Authentication;
require Catalyst::Plugin::Session;
require Catalyst::Plugin::Session::Store::FastMmap;
require Catalyst::Plugin::Session::State::Cookie;
require Catalyst::Plugin::I18N;
require Catalyst::Plugin::Static::Simple;
require Catalyst::Plugin::RequestToken;
require Catalyst::Controller::HTML::FormFu;
require Catalyst::Plugin::Unicode;
require Catalyst::Plugin::Email::Japanese;

pod2usage(2) unless @ARGV >= 1;

my $module = shift @ARGV;

# from pmsetup
# $module = "Foo::Bar"
# $dist   = "Foo-Bar"
# $path   = "Foo/Bar.pm"
# $pkg_dir   = "Foo/Bar"
my @pkg  = split /::/, $module;
my $dist = join "-", @pkg;
my $path = join( "/", @pkg ) . ".pm";
my $appprefix = Catalyst::Utils::appprefix($module);
my $pkg_dir = join( "/", @pkg );

my %templates;
my %chunks = grep { $_ } split /(?:^\s*)?=== (\w+) ===/sm, do { local $/; <DATA> };

while (my ($key, $chunk) = each %chunks) {

    for my $text (grep /\S+/, split /^---/m, $chunk) {
        my $obj;
        eval {
            $obj = YAML::Load( $text );
        };
        if ($@) {
            warn $@;
            next;
        }

        if ($obj->{file}) {
            $obj->{file} = eval "qq{$obj->{file}}"
                or die $@;
        }

        push @{ $templates{$key} }, $obj;
    }

}

sub render {
    my $name = shift;
    my $templates = $templates{$name};

    my $base_dir = dir( $dist );

    my $tt = Template->new;
    my $vars = {
        module    => $module,
        pkg       => \@pkg,
        dist      => $dist,
        path      => $path,
        appprefix => $appprefix,
	pkg_dir   => $pkg_dir
    };

    for my $t (@$templates) {
        my $file;
        if ($t->{file}) {
            $file = $base_dir->file( $t->{file} );
        }
        elsif ($t->{component}) {
            (my $p = $path) =~ s/\.pm$//;
            $file = $base_dir->file( 'lib', $p, $t->{component} );
        }
        next unless $file;

	my $content;
	if($t->{ignore}) {
	  $content = $t->{template}; 
        } else {
          $tt->process( \$t->{template}, $vars, \$content );
        }
        
	my $op = '>';
        if ( ($t->{type} || '') eq 'append' ) {
            $op = '>>';
        }

        print $op eq '>>' ? "Updating $file\n" : "Creating $file\n";
        if (!-d $file->parent) {
            $file->parent->mkpath or die $!;
        }

        open my $out, $op, $file or die "$file: $!";
        print $out $content;
        close $out;
    }
}

if ( $opt{new} ) {
    !system "catalyst.pl $module"   or die $?;
    !system "rm -rf $dist/root/*"    or die $?;
    !system "mkdir $dist/tmp"       or die $?;
    !system "mkdir -p $dist/root/static/css" or die $?;
    !system "mkdir -p $dist/root/static/images" or die $?;
    !system "mkdir -p $dist/root/static/js" or die $?;
    !system "mkdir -p $dist/root/templates" or die $?;
    !system "mkdir -p $dist/root/templates/common/site" or die $?;
    !system "mkdir -p $dist/root/templates/common/config" or die $?;
    !system "mkdir -p $dist/root/templates/errors" or die $?;
    !system "mkdir -p $dist/root/templates/email" or die $?;
    !system "mkdir -p $dist/root/forms" or die $?;
    !system "mkdir -p $dist/sql" or die $?;
    !system "mkdir -p $dist/lib/$pkg_dir/I18N/" or die $?;
    render('core');

    !system "./$dist/script/$appprefix\_create.pl view TT TT::ForceUTF8" or die $?;
}
elsif ( !-d $dist ) {
    warn qq{No such directory "$dist". Please run "catsetup.pl $dist -new" first.\n};
    exit;
}

for my $option (@render_options) {

    if ($option eq 'json') {
        !system "./$dist/script/$appprefix\_create.pl view JSON JSON" or die $?;
    }

    if ($option eq 'db') {
        !system "./$dist/script/$appprefix\_create.pl model DBIC DBIC::Schema $module\::Schema" or die $?;
        !system "mkdir -p ./$dist/schema/lib/$pkg_dir/Schema" or die $?;
    }
    render($option);
}

__DATA__

=== core ===
---
file: 'lib/$path'
template: |
  package [% module %];
  use strict;
  use warnings;
  
  use Catalyst::Runtime '5.70';
  use Catalyst qw/
      -Debug
      ConfigLoader
      FillInForm::ForceUTF8
      Authentication
      Session
      Session::Store::FastMmap
      Session::State::Cookie
      Email::Japanese
      Unicode
      I18N
      RequestToken
      Static::Simple
      /;
  our $VERSION = '0.01';
  
  __PACKAGE__->setup;
  
  1;
  
---
file: '$appprefix.yml'
template: |
  ---
  name: [% module %]
  page_title: 'Title' 
  default_view: TT
  View::TT:
    INCLUDE_PATH: __path_to(root/templates)__
    WRAPPER: 'common/site/wrapper.tt2'
  
---
component: Controller/Root.pm
template: |
  package [% module %]::Controller::Root;
  use strict;
  use warnings;
  use base 'Catalyst::Controller';
  __PACKAGE__->config->{namespace} = '';
  
  sub default :Private {
      my ($self, $c) = @_;
      $c->res->status(404);
      $c->stash->{template} = 'errors/404.tt2';
  }
  
  sub index :Private {
      my ($self, $c) = @_;
      $c->stash->{template} = 'index.tt2';
  }
  
  sub end :ActionClass('RenderView') {
      my ($self, $c) = @_;
      $c->res->header( 'Cache-Control' => 'private' );
  }
  
  1;

=== perllib ===
---
file: 'lib/$pkg_dir/Data.pm'
template: |
  package [% module %]::Date;
  
  use strict;
  use warnings;
  use base qw( DateTime );
  
  sub now {
      my($class, %opt) = @_;
      my $self = $class->SUPER::now();
      $self->set_time_zone('Asia/Tokyo');
      $self;
  }
  
  sub parse {
      my ( $class, $format, $date ) = @_;
  
      my $module;
      if (ref $format) {
          $module = $format;
      } else {
          $module = "DateTime::Format::$format";
          $module->require or die $@;
      }
  
      my $dt = $module->parse_datetime($date) or return;
      if ($dt->time_zone->is_floating) {
          $dt->set_time_zone( 'Asia/Tokyo' );
      }
      bless $dt, $class;
  }
  
  sub format {
      my ( $self, $format ) = @_;
      my $module;
      if (ref $format) {
          $module = $format;
      } else {
          $module = "DateTime::Format::$format";
          $module->require or die $@;
      }
  
      $module->format_datetime($self);
  }
  
  1;
  
=== json ===
---
file: '$appprefix.yml'
type: append
template: |+2
  
  View::JSON:
    expose_stash: 'json'
  #  json_driver: XS
    no_x_json_header: 1
  
---
component: Controller/API.pm
template: |
  package [% module %]::Controller::API;
  use strict;
  use warnings;
  use base 'Catalyst::Controller';
  
  sub end :Private {
      my ($self, $c) = @_;
  
      my $json = $c->stash->{json} ||= {};
      $json->{id} = $c->req->param('id');
      $json->{result} ||= '' unless defined $json->{result};
      $json->{error}  ||= '' unless defined $json->{error};
  
      $c->stash->{fillform} = 0;
  
      $c->forward( $c->view('JSON') );
  }
  
  1;

=== db ===
---
file: '$appprefix.yml'
type: append
template: |+2
  
  Model::DBIC:
    connect_info:
      - dbi:mysql:[% appprefix %]
      - root
      - on_connect_do:
          - SET NAMES utf8
  
=== email ===
---
file: '$appprefix.yml'
type: append
template: |+2
  
  email:
    TmplOptions:
      INCLUDE_PATH: __path_to(root/templates/email)__
  #  ForceUTF8: 1
    mailroute:
      via: sendmail
      command: /usr/sbin/sendmail
    From: noreply@example.com

=== authentication ===
  authentication:
    default_realm : 'members'
    realms:
      members:
        credential:
          class: Password
          password_field: password
          password_type: hashed
          password_hash_type: SHA-1
        store:
          class: 'DBIx::Class'
          user_class: 'DBIC::User'
          id_field: 'username'
	
=== cache ===
---
file: '$appprefix.yml'
type: append
template: |+2

  cache:
    backend:
      store: FastMmap
      share_file: __path_to(tmp/cache)__

=== i18n ===
---
file: 'script/update-po.sh'
template: |
  #!/bin/sh
  (find templates/ -name '*.tt2' ; find lib/[% pkg_dir %]/Controller -name '*.pm' )| xargs xgettext.pl -o lib/[% pkg_dir %]/I18N/ja.po

=== templates ===
---
file: 'root/templates/common/site/header.tt2'
template: |

---
file: 'root/templates/common/site/footer.tt2'
template: |
  <div id="copyright">&copy; dann. All rights reserved.</div>
 
---
file: 'root/templates/common/site/layout.tt2'
ignore: 1
template: |
  <div id="container">
  <div id="header">[% PROCESS common/site/header.tt2 %]</div>
  <div id="content">
  [% content %]
  </div>
  <div id="footer">[% PROCESS common/site/footer.tt2 %]</div>
  </div>
  
---
file: 'root/templates/common/site/html.tt2'
ignore: 1
template: |
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>[% c.config.page_title %][% IF title %] - [% title | html %][% ELSE %] - Example -[% END %]</title>
  <link rel="alternate" type="application/atom+xml" title="Atom" href="[% c.uri_for('/feed') %]" />
  <link rel="stylesheet" href="[% c.uri_for('/static/css/common/default.css') %]" type="text/css" />
  <link rel="stylesheet" href="[% c.uri_for('/static/css/common/common.css') %]" type="text/css" />
  <link rel="stylesheet" href="[% c.uri_for('/static/css/site.css') %]" type="text/css" />
  <script type="text/javascript" src="[% c.uri_for('/static/js/site.js') %]"></script>
  </head>
  <body>
  [% content %]
  </body>
  </html>

---
file: 'root/templates/common/site/wrapper.tt2'
ignore: 1
template: |
  [% IF template.name.match('\.(css|js|txt)');
       debug("Passing page through as text: $template.name");
       content;
     ELSE;
       debug("Applying HTML page layout wrappers to $template.name\n");
       content WRAPPER common/site/html.tt2 + common/site/layout.tt2;
     END;
  -%] 
  
---
file: 'root/templates/index.tt2'
template: |
  Hello World

=== helper ===
---
file: 'script/$appprefix\_schema\_dumper.pl'
template: |
  #!/usr/bin/env perl
  
  use strict;
  use warnings;
  
  use FindBin;
  use File::Spec;
  use lib File::Spec->catfile( $FindBin::Bin, qw/.. schema lib/ );
  
  use Path::Class qw/file dir/;
  use DBIx::Class::Schema::Loader qw/make_schema_at/;
  
  my @arg = defined @ARGV ? @ARGV : qw();
  
  die unless @arg;
  
  # do manual delete instead of really_erase_my_files option
  #    for keep MyApp/Schema.pm
  my $libdir = dir($FindBin::Bin, 'lib', 'OpenLine', 'Schema' );
  if (-d $libdir) {
      $libdir->rmtree;
  }
  
  make_schema_at(
      'OpenLine::Schema',
      {   components => [ 'ResultSetManager', 'UTF8Columns' ],
          dump_directory => File::Spec->catfile( $FindBin::Bin, '..', 'lib' ),
  	really_erase_my_files => 1,
          debug          => 1,
      },
      \@arg,
  );
  
  1;


=== css ===
---
file: 'root/static/css/common/default.css'
template: |
  @charset "utf-8";
  	
  /*======================================
  
  	1-1.Yahoo UI Library Fonts CSS ver.2.3.1
  	http://developer.yahoo.com/yui/fonts/
  	*Copyright (c) 2006, Yahoo! Inc. All rights reserved.
  	*http://developer.yahoo.com/yui/license.txt
  
  	Font-size Adjustment
  	
  	77% = 10px	| 	122% = 16px	|	167% = 22px
  	85% = 11px	|	129% = 17px	|	174% = 23px
  	92% = 12px	|	136% = 18px	|	182% = 24px
  	100% = 13px	|	144% = 19px	|	189% = 25px
  	107% = 14px	|	152% = 20px	|	197% = 26px
  	114% = 15px	|	159% = 21px	|
  
  =======================================*/
  
  /*編集不要*/
  body {font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:99%;}
  
  /*======================================
  
  	1-2.Universal selector
  
  =======================================*/
  
  * {
  	line-height: 1.6;
  	font-size: 100%;
  	font-weight: normal;
  	font-style: normal;
  }
  
  /*======================================
  
  	1-3.Structure Module
  
  =======================================*/
  
  body {
  	color: #333;
  	background-color: #fff;
  	font-family: Arial, Helvetica, sans-serif;
  }
  
  /*======================================
  
  	1-4.Text Module
  
  =======================================*/
  
  p,
  pre,
  address,
  cite {
  	margin: 0.5em 20px;
  	font-size: 100%;
  }
  
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
  	margin: 0.5em 20px; 
  }
  
  h1 {
  	font-size: 189%;
  }
  
  h2 {
  	font-size: 159%;
  }
  
  h3 {
  	font-size: 144%;
  }
  
  h4 {
  	font-size: 122%;
  }
  
  h5 {
  	font-size: 107%;
  }
  
  h6 {
  	font-size: 107%;
  }
  
  pre {
  	padding: 0.5em 10px;
  	border: 1px dotted #aaa;
  	width: 90%;
  	overflow: scroll;
  	color: #333;
  	background-color: #f5f5f5;
  	font-family: "Osaka-等幅", monospace;
  }
  
  pre[title]::before {
  	margin-bottom: 0.8em;
  	padding: 0 10px;
  	display: block; 
  	content: attr(title);
  	color: #000;
  	background-color: #fff;
  }
  
  blockquote {
  	margin: 1.5em 20px;
  	padding: 1px 0; 
  	border: 3px solid #eee;
  	background-color: #fff;
  }
  
  blockquote * {
  	color: #666;
  }
  
  blockquote[title]:before {
  	margin: 2px 2px 1em 2px;
  	padding: 0.1em 16px;
  	display: block;
  	content: attr(title); 
  	background-color: #f5f5f5; 
  }
  
  blockquote[cite]:after {
  	padding: 0.8em 20px;
  	display: block; 
  	content: attr(cite);
  	color: #333;
  	text-align: right;
  }
  
  cite {
  	display: block;
  	color: #333;
  	text-align: right;
  }
  
  em {
  	font-weight: bold;
  }
  
  strong {
  	color: #ff4500;
  }
  
  code {
  	font-family: "Osaka-等幅", monospace;
  }
  
  abbr,
  acronym {
  	border-bottom: 1px dotted #aaa;
  	cursor: help;
  }
  
  kbd {
  	border: 1px solid #ccc;
  	padding: 0 0.3em; 
  	background-color: #f5f5f5;
  	font-family: "Osaka-等幅", monospace;
  	text-transform: uppercase;
  }
  
  /*======================================
  
  	1-5.Hypertext Module
  
  =======================================*/
  
  a:link {
  	color: #005585;
  }
  
  a:visited {
  	color: #818f98;
  }
  
  a:hover {
  	color: #80af00;
  }
  
  /*======================================
  
  	1-6.List Module
  
  =======================================*/
  
  ul,
  ol,
  dl {
  	margin: 1em 20px;
  	padding: 1px 0;
  	list-style-position: inside;
  }
  
  li,
  dt,
  dd {
  	margin: 0.1em 10px;
  }
  
  dt {
  	margin-top: 0.6em;
  }
  
  dd {
  	margin-bottom: 0.6em;
  	color: #666;
  }
  
  li li,
  li p,
  li pre,
  li dt,
  li dd,
  dd li,
  dd p,
  dd pre,
  dd dt,
  dd dd {
  	font-size: 100%;
  }
  
  li ul,
  li ol,
  li dl,
  li p,
  dd ul,
  dd ol,
  dd dl,
  dd p {
  	margin: 0.1em 10px;
  }
  
  /*======================================
  
  	1-7.Edit Module
  
  =======================================*/
  
  del {
  	color: #999;
  	text-decoration: line-through;
  }
  
  del[datetime]::before {
  	content: " ( "attr(datetime)"\00524a\009664) ";
  }
  
  ins {
  	border-bottom: 1px dotted #ccc;
  	text-decoration: none;
  }
  
  ins[datetime]::before {
  	content: " ( "attr(datetime)"\004fee\006b63) ";
  }
  
  /*======================================
  
  	1-8.Forms Module
  
  =======================================*/
  
  form {
  	margin: 0.5em 20px;
  	padding: 1px 0; 
  }
  
  form dl,
  form p {
  	margin: 0.5em 10px;
  }
  
  fieldset {
  	border: 1px solid #ddd;
  }
  
  legend {
  	margin: 0 1em;
  	padding: 0 10px;
  }
  
  input,
  textarea {
  	margin: 0.4em 10px;
  	padding: 0.1em 10px;
  	border: 1px solid #ddd;
  	font-family: Arial, Helvetica, "ヒラギノ角ゴ Pro W3",  sans-serif;
  	background-color: #f5f5f5;
  }
  
  input {
  	line-height: 1.2;
  }
  
  input:hover,
  textarea:hover {
  	border: 1px solid #aaa;
  }
  
  input:focus,
  textarea:focus {
  	border: 1px solid #000;
  }
  
  textarea {
  	padding: 0.4em 10px;
  }
  
  /*======================================
  
  	1-9.Tables Module
  
  =======================================*/
  
  table {
  	margin: 0 20px 0.5em 20px;
  	border-collapse: separate;
  	border-spacing: 1px;
  	border: 1px solid #cfd3d6;
  	background-color: #fff;
  }
  
  th,
  td {
  	padding: 0.1em 5px;
  	border: 1px solid #efefef;
  	border-color: #efefef #dce0e3 #dce0e3 #efefef;
  }
  
  th {
  	color: #000;
  	background-color: #eff0f1;
  }
  
  td {
  	border: 1px solid #ddd;
  	background-color: #fff;
  }
  
  /*======================================
  
  	1-10.Image Module
  
  =======================================*/
  
  img {
  	vertical-align: bottom;
  }
  
  a img {
  
  }
  
  a:hover img {
  
  }
  
  /*======================================
  
  	1-11.Object Module
  
  =======================================*/
  
  object,
  embed {
  	margin: 1em 20px;
  }

---
file: 'root/static/css/common/common.css'
template: |
  @charset "utf-8";

---
file: 'root/static/css/site.css'
template: |
  @charset "utf-8";


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