2012年12月7日金曜日

BanchaとBanchaScaffoldを利用した簡単なCRUD実装のまとめ

BanchaとBanchaScaffoldを利用した簡単なCRUD実装のまとめ



この記事は、Sencha Advent Calendar 2012
の7日目の記事です。
皆さんも是非、是非、参加してください。後半、まだまだ参加枠ありますよ!!そこのあなた!!



数カ月前にBanchaを試した時の備忘録を残してあったんですが、

一つのテーブルに対するCRUD実装を通して--
Scaffoldを利用するまでをまとめて紹介してみます。

ほとんど過去の備忘録の焼き直しですけど、、 


Banchaってなんだろうか



  • PHPのフレームワークであるCakePHP2.0に対応したExtJs用のプラグイン

  • ExtDirect用のモデル(Proxy設定含む)を生成してくれる

というのが主なところだと思います。 

ExtDirectについては@martini3ozさんのブログでわかりやすい
解説がされているのでそちらを参照して下さい。
他にExtDirectに対応するPHPフレームワークだと株式会社ゼノフィが
作成しているxFrameworkPXがあります。
Directについて理解を深めたいのであればそちらを利用し、コードを
眺めてみると仕組みを深く理解できると思います。

サーバサイドはCake2.Xで組みたい。

フロントエンドはExtJs作りこみたい。

ExtJsDirectを気軽に使いたい。

そんな方にはオススメできるかもしれません。 

BanchaScaffoldってなんだろうか


ExtJsにおけるデータの入出力の基本的な機能として、Grid、Formがあります。

ともに、ExtDirectを利用する際には、Model(Proxy含む)やStoreを設定したり、

サーバサイドのメソッド名を設定する必要があります。

このあたりを吸収して、「ある程度」の雛形を提供してくれるのが

BanchaScaffoldです。 

Grid、Formをオーバーライドして特定のパラメータに

反応するように作成されています。

ですので構造さえ知ってしまえば細かな変更などができると思うので

便利な機能になるんじゃないかと思っています。


Banchaの情報


http://banchaproject.org/resources.html

このページがすべてのリソースが集約されててお勧めかもです。 

CRUD実装の大きな流れ


このお話の大きな流れは、

1:Cake2.Xの準備

2:ExtJsの準備

3:MySQLの準備

4:Banchaの準備

5:Bakeの実行

6:Scaffoldの利用

7:動作確認 

になります。

前提


MAMPでUserDir配下で試してます。

今回は最新版を試している時間がないので、以前試したことのある

* Bancha1.0.0

* BanchaScaffold0.5.6

* Cake2.1.5

* ExtJs4.1.1a

の組み合わせでお話をすすめます。 

今日(2012/12/7)現在、Banchaは1.2.3がBanchaScaffoldは0.6.0が最新のようです。  
CakeのDispacharの実装が2.1系と2.2系で異なるため、BanchaとCakeの組み合わせには  
注意が必要です。


1:Cake2.0の準備





ユーザディレクトリの配置


私の場合、ユーザ配下のUserDirがユーザディレクトリのルートになっているので

その下にadventcalendarとでも作っておきましょう。 

[ユーザ]/UserDir/adventcalendar  

で、ルートを public_html としてあるので、それも作っておきます。 

[ユーザ]/UserDir/adventcalendar/public_html  

Cake2.XのDL


2.1.5をDLしてきます。

過去分なので、githubからタグされたバージョンで持ってきちゃいました。

こちらから

解凍して public_html 配下にまるっとコピーしておきます。 

▾ public_html/
  ▸ app/
  ▸ lib/
  ▸ plugins/
  ▸ vendors/
    .gitignore
    .htaccess
    .travis.yml
    build.properties
    build.xml
    index.php
    README

ユーザディレクトリ使ってるのでCakeの階層にしたがってRewriteBaseしてあげます。

このあたりはご利用になっている環境にあわせて適宜に設定してみて下さい。

過去の備忘録 

saltとseedの設定もしておきましょね。


2:ExtJsの準備





ExtJs4.1.1aのDL


ExtJsをDLしてきます。

解凍して適宜に配置します。 

ExtJSのスタートアップはゼノフィの技術情報ページや  
@martini3ozさんのページ、  
本家のチュートリアルを参照することをおすすめします。  

今回は public_html/app/webroot 配下に ext として配置しました。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ css/
      ▸ ext/    <--ここ
      ▸ files/
      ▸ img/
      ▸ js/
        .htaccess
        favicon.ico
        index.php
        test.php
      .htaccess
      index.php
  ▸ lib/
  ▸ plugins/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~



設置用のindex.htmlの配置


MVC構成にするわけじゃなくて単なる表示確認です。

とりあえずパスが通ってることだけ確認しておきます。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ css/
      ▸ ext/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~
      ▸ files/
      ▸ img/
      ▸ js/
        .htaccess
        bancha.php
        favicon.ico
        index.html  <--ここ
        index.php
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~



404が出てないことだけ確認しておきましょ。

設置用のindex.htmlの内容


index.html  
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <script type="text/javascript" charset="utf-8" src="./ext/ext-all.js"></script>
    </head>
    <body>
        <p>hoge</p>
    </body>
</html>


3:MySQLの準備





DB、テーブル作成


次にDBを作ってMySQLにサンプル用のテーブルを作ってみましょう。 

DB名:adventcalendar
CREATE TABLE `calender` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `date` date NOT NULL,
  `acount` varchar(255) NOT NULL,
  `comment` text NOT NULL,
  `created` date NOT NULL,
  `modified` date NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

こんな具合でしょうか。 

Cakeへの設定


Cakeに利用するDB情報を設定します。

/app/Config/.database.php.defaultがあるので

/app/Config/.database.php

とコピーしてから、中身を書き換えます。 

public $default = array(
    'datasource' => 'Database/Mysql',
    'persistent' => false,
    'host' => 'localhost',
    'login' => '*****',
    'password' => '*****',
    'database' => 'adventcalendar',
    'encoding' => 'utf8',
);



▾ public_html/
  ▾ app/
    ▾ Config/
      ▸ Schema/
        acl.ini.php
        acl.php
        bootstrap.php
        core.php
        database.php  <-- ここ
        database.php.default




4:Banchaの準備





Bancha1.0.0をDLしてきます。

過去分なので、githubからタグされたバージョンで持ってきます。

こちらから。 

Banchaの設置


解凍して public_html/app/Plugin/Bancha に配置します。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    略
    ~~~~~~~~~~~~~~~
    ▸ Model/
    ▾ Plugin/
      ▾ Bancha/  <--ここ
        ▸ _app/
        ▸ Config/
        ~~~~~~~~~~~~~~~
        略
        ~~~~~~~~~~~~~~~
    ▸ Test/
    ▸ tmp/
    ~~~~~~~~~~~~~~~
    略
    ~~~~~~~~~~~~~~~



プラグインの設定


プラグインの設定はこちらを参考に行います。 

/public_html/app/Config/bootstrap.phpに 

CakePlugin::load(array('Bancha' => array('routes' => true, 'bootstrap' => true)));

/publichtml/app/Plugin/Bancha/app/webroot/bancha.php



/public_html/app/webroot/bancha.php

にコピー。 

public_html/app/Config/core.phpに 

/**
 * Configure the cache for Banchas Remote API.
 */
Cache::config('_bancha_api_', array(
    'engine' => $engine,
    'prefix' => $prefix . 'bancha_api_',
    'path' => CACHE . 'models' . DS,
    'serialize' => ($engine === 'File'),
    'duration' => $duration
));

を追記。 

確認


設定がうまく行っているか確認します。 

http://localhost/~adventcalendar/Bancha/setup-check.html

こんな具合になればOKです。 

bancha疎通確認

5:Bakeの実行





ここまで来るとあとは実装が残るだけとなります。

Bakeというバッチを使用してCakePHPのModel,Contollerを作成します。 


長くなってしまうので省略しますが、ヤルことは

こちらとほぼ一緒です。 

コンソールからappフォルダ配下に移動してbakeを実行します。 

cd UserDir/public_html/app
./Console/cake bake

DBの接続情報を作る


(cake設定時に作成しているのであればここでは作らなくてもOK)

Modelを作る[M]


(作ったあとに、モデルに下記[<--ここ]を追加して下さい。これによりモデルが公開されることになります。)
Calendar.php
<?php
App::uses('AppModel', 'Model');
/**
 * Calendar Model
 *
 */
class Calendar extends AppModel {

    /**
     * Bancha behaviour
     */
    public $actsAs = array('Bancha.BanchaRemotable');  <-- ここ
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~

Controllerを作る[C]


(Would you like to create some basic class methods  
(index(), add(), view(), edit())? (y/n) 
と聞かれた時に「y」として下さい。)  
(templateの選択を問われますので「bancha」を選択して下さい)



▾ public_html/
  ▾ app/
    ▸ Config/
    ▸ Console/
    ▾ Controller/
      ▸ Component/
        AppController.php
        CalendarsController.php  <--ここ
        ContollersController.php
        PagesController.php
    ▸ Lib/
    ▸ Locale/
    ▾ Model/
      ▸ Behavior/
      ▸ Datasource/
        AppModel.php
        Calendar.php  <--ここ
    ▸ Plugin/
    ▸ Test/
    ▸ tmp/
    ▸ Vendor/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~



ここまででサーバサイドの設定などの準備は完了です。


6:Scaffoldの利用





Scaffoldの準備


ScaffoldをDLしてきます(0.5.6)。

webroot配下に設置しました。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▾ bancha/ <-- ここの配下のこと
          bancha-scaffold-debug.js*
          bancha-scaffold-production.js*
          bancha-scaffold.js*
      ▸ css/
      ▸ ext/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~



Extjsの準備


MVC構成としてのフォルダ配置をしておきます。

フォルダ構成の話とかだと、、手っ取り早いのは

ゼノフィの技術者ブログか 

ExtJsのサンプル:

public_html/app/webroot/ext/examples/MVC/pandora/ 

を持ってきてもいいかと思います。ちょっとここは省略。



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▾ webroot/
      ▸ css/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
      ▾ js/
        ▾ app/  <--ここの配下のことです。
          ▾ controller/
          ▾ model/
          ▾ store/
          ▸ view/
            app.js
          empty
        .htaccess



index.htmlへの準備


Banchaを利用するため


  • Bancha.jsを読み込みます

  • bancha-api.jsを読み込みます

BanchaScaffoldを利用するため


  • bancha-scaffold.jsを読みます



<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <script type="text/javascript" charset="utf-8" src="./ext/ext-all.js"></script>
        <!-- include Bancha and the remote API -->
        <script type="text/javascript" charset="utf-8" src="Bancha/js/Bancha.js"></script>      <--ここ
        <script type="text/javascript" charset="utf-8" src="bancha-api.js"></script>            <--ここ
        <!-- include Bancha and the remote API -->
        <script type="text/javascript" charset="utf-8" src="bancha/bancha-scaffold.js"></script><--ここ
        <script type="text/javascript" charset="utf-8" src="bancha-api.js"></script>
        <script type="text/javascript" src="./js/app/app.js"></script>
    </head>
    <body>
    </body>
</html>



いよいよScaffoldを利用します


Calendar用のGridを一つ作成します。 

Calendargrid.js

Ext.define('ADV.view.Calendargrid', {
    extend : 'Ext.grid.Panel',
    alias  : 'widget.calendargrid',
    scaffold :'Bancha.model.Calendar'  <-- ここがみそ。
});

Viewportに設置します。

Viewport.js

Ext.define('ADV.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires: [
        'ADV.view.Calendargrid',
    ],
    initComponent: function() {
        this.items = [{
            xtype:'calendargrid'
        }];
        this.callParent();
    }
});

CSSファイル作って削除用ボタンのパスを通しておきます。

style.css

.icon-add{
    background-image: url(../icons/add.png) !important;
}
.icon-delete{
    background-image: url(../icons/delete.png) !important;
}
.icon-save{
    background-image: url(../icons/disk.png) !important;
}
.icon-reset{
    background-image: url(../icons/arrow_undo.png) !important;
}

index.html

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <link rel="stylesheet" type="text/css" href="./js/resources/css/style.css"/>  <--ここ
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~

あと、Grid内のactioncolumnの画像はパス指定になっているので
それも変更します。(Installationに載ってました。。)

app.js

Ext.Loader.setConfig({
    enabled: true,
    paths: {
        'Ext': './ext/src',
        'ADV': './js/app'
    }
});
Bancha.onModelReady([
    // ここにモデル名指定
    'Calendar'
        ],function(){
    // {{{ Ext.application
    Ext.application({
        name: 'ADV',
        appFolder: './js/app',
        autoCreateViewport: true,
        launch: function() {
            alert('app launch!!');
        }
    });
});
Bancha.scaffold.Grid.destroyButtonConfig.items[0].icon =
                  './js/resources/icons/delete.png';



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ bancha/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~
      ▾ js/
        ▾ app/
          ▸ controller/
          ▸ model/
          ▸ store/
          ▾ view/
              Calendargrid.js   <-- ここ
              Viewport.js       <-- ここ
            app.js              <-- ここ
        ▾ resources/
          ▾ css/
              style.css         <-- ここ
          ▾ icons/              <-- ここ
              add.png           
              arrow_undo.png
              delete.png
              disk.png
          empty
        .htaccess
        bancha.php
        favicon.ico
        index.html              <-- ここ
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~




7:確認





こんな感じになると思います。

実行画面


画面はまだ簡素ですが、

これでサーバとの通信ができることになります。

とっても単純なCRUD実装ですが、

独自のカスタマイズができるように

基底クラスを作成できれば使いやすくなりますね。 


scaffoldはformにも対応しているので

さっと作りたいときなどは重宝するのではないでしょうか。 


あとは慣れてきたら

APIドキュメント見ながらソースと照らし合わせながら

遊んでみると面白いと思います。。

http://scaffold.banchaproject.org/

http://scaffold.banchaproject.org/docs/# 



駆け足でしたが

こんな具合でBanchaはお手軽に導入できます。

っということで@martini3ozさんからのフリがあったのでつないでみました。 


このBanchaは先の@marini3ozさんの記事、Proxyについてを

読んでからみるとなかなかおもしろいと思います。 


ほんとは、このあとBanchaScaffoldをいじってカスタマイズする記事を

書きたかったのですが、今、高熱出してまして続きが書けませんでした。。

もうむりっす。。 ごめんなさい。


さてさて、残すは青さんかな(笑 

2012年9月18日火曜日

bancha scaffoldで独自のボタンを追加するときのscope設定(this)

Scaffoldのbuttonsに独自のボタンを設定するとき、 scope:'scaffold-scope-me'を指定。 するとthisはgetPanel(),getForm()を有するオブジェクトを返すから、 そっからfireEventしてContorllerで引っ掛ける。
buttons:[{
    text: 'search',
    scope: 'scaffold-scope-me',
    handler : function(){
        var me = this;
        me.getPanel().fireEvent('search');
    }
}],
Scaffoldで検索用のフォームとか、独自のボタンをくっつけるときのこと。

2012年9月8日土曜日

markdown2impressを触ってみた。

社内でちょっとしたことをやるのでパワポ使ってもいいんですがせっかくだし、Markdown紹介がてら。。なぁ。。。

とおもって少しだけ変わったことでもしようかと。

markdownをimpressjs化する方法が こちらや、 こちらで紹介されていたのでこれをやってみようかと思います。 参考にさせていただきました。ありがとうございます!

両サイトを参考にさせてもらいつつ、試してみます。

環境

perlって入ってるんだっけか?

まずはそこなんだけど、ゴリゴリ入れます。

$sudo port install perl5

環境入れます。

git://github.com/yoshiki/markdown2impress.git

をクローンします。それだけ。

$git clone git://github.com/yoshiki/markdown2impress.github

実行して環境が整ってないことで怒られる。

$ ./bin/markdown2impress.pl README.md                                  12-09-08
Can't locate Data/Section/Simple.pm in @INC (@INC contains: /opt/local/lib/perl5/site_perl/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/site_perl/5.12.4 /opt/local/lib/perl5/vendor_perl/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/vendor_perl/5.12.4 /opt/local/lib/perl5/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/5.12.4 /opt/local/lib/perl5/site_perl /opt/local/lib/perl5/vendor_perl/5.12.3 /opt/local/lib/perl5/vendor_perl .) at ./bin/markdown2impress.pl line 6.
BEGIN failed--compilation aborted at ./bin/markdown2impress.pl line 6.

いやーんな感じです。

が、 ホント助かります。CPANって言うアーカイブからインストールするんですね。。 超助かりますし、しらんこと多すぎだし。そもそもPerl環境すらわからんし。。 お世話になりました。こちら

実行しつつ、怒られたものをいれてきます。

$ sudo cpan Data/Section/Simple.pm 
$ sudo cpan Text/Markdown.pm
$ sudo cpan Text/Xslate.pm
$ sudo cpan Path/Class.pm

実行した結果。

$ ./bin/markdown2impress.pl README.md                                  12-09-08
froggugugugu@sasakiMBP:~/markdown2impress
$ ls                                                                   12-09-08
README.md   bin     css     index.html  js

見てみた。

sugeeee.

これはこれからも使わせて頂きます。 たのしいわ。

2012年8月27日月曜日

bancha scaffoldを触ってみた

Scaffoldを

Banchaをググってたらフォーラムに到着して
出会いました。
見てみたら、マーケットにありますね。。 早速ソースをDLしてざっと眺めるとどうやら
Banchaの仕様に適した(サンプルをデフォルトで実装してくれる様な)
コンフィグビルダ的なプロジェクトでした。 ざっくり言うとExt.grid.Panelをオーバライドして scaffoldコンフィグに反応するようにしてあって BanchaにあったGridコンフィグ、Formコンフィグを作ってくれる 仕組みです。 独自の実装はbeforeBuildなり、afterBuildなりで調整実装します。 早速お試しを。

設置

どこに設置するのが良いのかわからなかったけど、 ひとまずextと同階層に設置することしました。
▾ banchajs/ <--(これ)
    bancha-scaffold-debug.js
    bancha-scaffold-production.js
    bancha-scaffold.js
▸ css/
▸ ext/
▸ files/
▸ img/
▸ js/
  .DS_Store
  .htaccess
  bancha.php*
  favicon.ico
  index.html
  index.php
  test.php

ロード

index.htmlに追加します。しばらくはコード見つつ使うと思うのでデバグ版です。
<script type="text/javascript" charset="utf-8" src="./banchajs/bancha-scaffold-debug.js"></script>

サンプルを改造(View)

ビルドコンフィグ内で、デフォルトの設定をしてくれているので なんもいじらんのであれば、、
// {{{ 'BE.view.Usergrid'
/**
 * @class BE.view.Usergrid
 * desctription
 */
Ext.define('BE.view.Usergrid', {
    // {{{ extend

    extend : 'Ext.grid.Panel',

    // }}}
    // {{{ alias

    alias : 'widget.usergrid',

    scaffold :'Bancha.model.User'

    // }}}
});
// }}}
これだけでOK。そう、これだけでOKです。 scaffoldにBanchaが提供するモデル名を設定します。
※このscaffold、オブジェクト指定でscaffold:{target:'Bancha.model.User'}として 実装することも可能ですが、その際はModelManagerにすでに登録されていることが前提となるので 何らかの問題で引っかかったときはこのあたりを見てみるといいかもです。
このパネルのinitConponentなりでBancha.getModel('User')とかして ModelManagerに登録しておくひつようがありますね。。

Store(サンプルを改造)

実はストアはBanchaのScaffoldがModelを利用して生成してくれるので Storeは必要ありません。削除しちゃいます。(わかりやすくコメントアウトとしておきます)

Controller(サンプルを改造)

各ボタンの実装もハンドラで直接結びつけちゃっているので この例だとコントローラも必要なくなっちゃいます。 (通常は必要になるとは思うのですけど)

実行してみる(画像が出ない)

画像が出ない。。。 そう、内部で設定される画像の設定、パスが通ってません。 icon-add、icon-save、icon-reset、icon-deleteをCSSに定義しておきます。
.icon-add {
    background-image: url(../icons/add.png) ! important;
}
.icon-delete: {
    background-image: url('../icons/delete.png') ! important;
}
.icon-save {
    background-image: url('../icons/disk.png') ! important; 
}
.icon-reset {
    background-image: url('../icons/arrow_undo.png') ! important; 
}
あと、Grid内のactioncolumnの画像はパス指定になっているので それを変更しなくてはなりません。。 なんかとりあえずの対応になってしまうけど、、
afterBuild : function (gridconfig) {

    // 削除ボタンコンフィグのreconfig
    var i;
    for (i = 0; i< gridconfig.columns.length; i++ ){
        if (gridconfig.columns[i].xtype == 'actioncolumn'){
            Ext.apply(gridconfig.columns[i].items[0],{icon:'js/src/resources/icons/delete.png'});
        }
    }
}
ひとまずxtypeで引っ掛けてパス変えちゃいました。
内部で固定設定されているメッセー文言とか、、
そとに口を引っ張りだして加工できるようにしたいですね。

userテーブルのBanchaScaffoldでの表示

現状況

▾ public_html/
  ▾ app/
    ▸ Config/
    ▸ Console/
    ▸ Controller/
    ▸ Lib/
    ▸ Locale/
    ▸ Model/
    ▸ Plugin/
    ▸ Test/
    ▸ tmp/
    ▸ Vendor/
    ▸ View/
    ▾ webroot/
      ▾ banchajs/
          bancha-scaffold-debug.js
          bancha-scaffold-production.js
          bancha-scaffold.js
      ▸ css/
      ▸ ext/
      ▸ files/
      ▸ img/
      ▾ js/
        ▾ src/
          ▾ app/
            ▾ controller/
                Main.js
                Usergrid.js
            ▾ model/
            ▾ store/
                user.js
            ▾ view/
                Usergrid.js
                Viewport.js
          ▾ resources/
            ▾ css/
                style.css
            ▾ icons/
                add.png*
                arrow_undo.png*
                delete.png*
                disk.png*
                image_add.png*
                user_edit.png*
            app.js
          empty
        .DS_Store
        .htaccess
        bancha.php*
        favicon.ico
        index.html
        index.php
        test.php
      .DS_Store
      .htaccess
      index.php
  ▸ lib/
  ▸ plugins/
  ▸ vendors/
    .gitignore
    .htaccess
    .travis.yml
    build.properties
    build.xml
    index.php
    README

2012年8月19日日曜日

banchaを触ってみた4

疎通させてみる

Bancha使ってみたくて
cakeをざっと流し読みしたけど、
まだ疎通できてないので疎通させてみようと思います。
お題はBanchaのサンプルをMVCに再配置、と、
テスト的にコンポーネントからイベント発火させて
Mainコントローラでディスパッチするようにしてみました。
A.View(何らかの操作)

Aコントローラ(ビューをコンポーネントクエリで引っ掛けて)
Ext既存イベントの処理→A.fireEventで独自イベント発行

Mainコントローラ(ビューをコンポーネントクエリで引っ掛けて)
独自インベントのディスパッチ
AコントローラをgetControllerで取得してAコントローラ.実装処理を呼び出し
なかんじで複数コントローラ時な感じを想定して見ました。

アプリの作成

ここからはExtJS4の話になります。
xframeworkであればwebapp下にsrcとか掘ってアプリを設置するんですが、
Cakeで言うところだと、、、
webroot/js/srcとかになるのでしょうかね。
あとは株式会社ゼノフィさんの技術ページ(code:x)か@martini3ozさんの
ページを見ながら実践開発ガイドのクラスローダ部分を読み返せばスタートアップ設置はOKです。
大掛かりな設置をすると大変なので
ビューポートにGridのみ入れて
Banchaのサンプルを切り出しながら再整形して
CRUDの確認だけしてみます。

example.htmlについて

やることは
  • extのロード
  • Banchaの準備
ということ。
<head>
    <title>Bancha Example</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <meta http-equiv="Content-Style-Type" content="text/css" /> 
    <meta http-equiv="Content-Script-Type" content="text/javascript" /> 

    <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css" />

    <script type="text/javascript" src="./ext/ext-all-debug.js"></script>
    <script type="text/javascript" src="./ext/locale/ext-lang-ja.js"></script>

    <!-- include Bancha and the remote API -->
    <script type="text/javascript" src="./Bancha/js/Bancha.js"></script>
    <!-- for the github version use /Bancha/js/Bancha.js -->
    <script type="text/javascript" src="./bancha-api/models/all.js"></script>

    <!-- ExampleApp の設置 -->
    <script type="text/javascript" src="./js/src/app.js"></script>

</head> 
<body> 
</body> 
</html>
こんな感じになるのでしょうか。 404帰ってきてないこと確認しておきます。

app.jsとviwportの設置

  • addProviderの関係上、先行してExt.OnReadyが走る必要があり、
    これはBancha.OnModelReady内でExt.OnRadyしている。  
ので、OnModelReadyのコールバックでExt.applicationしてやることに
してみました。
設置については、、
* snippetsでひな形配置して
* namespace決めて
* app名決めて
* launchにalertとか設置して動作確認。
* 最初はcontrollersプロパティをコメントアウトして表示される
のを確認してもいいかもしれないです(ブランク開くと表示すら躓くのでひとつづ表示させながら)
で、
こんなかんじになると思います。

  • app.js
アプリケーションについてはローダーの設定とか重要なところになります。
これは実践開発ガイドの77ページ目のローダの説明を見ると理解が深まります。
Ext.ns('BE');

// Ext.Loader有効化
Ext.Loader.setConfig({
    enabled: true,
    paths: {
        'Ext': './ext/src',
        'BE': './js/src'
    }
});

//Bancha 初期化
Bancha.onModelReady('User',function(){

    //alert('hoge');

    // アプリケーション設定
    Ext.application({

        // Viewport自動生成
        autoCreateViewport: true,

        // アプリケーション名
        name: 'BE',

        // アプリケーションフォルダパス
        appFolder: './js/src/app',

        // 使用コントローラー定義
        controllers: [
        'Main',
        'Usergrid'
        ],

        // アプリケーション起動時イベントハンドラ
        launch: function() {

            //alert('fuga');

        }

    });
});
サーバサイドのモデルが増えたら、、
onModelReadyの第一パラメタも増やしていきます。
Bancha.onModelReady(['User','hoge1','hoge2'],function(){
みたいにね。

  • viewport.js
ビューポートはCenterにグリッドのみ設置。xtypeで指定します。
// {{{ BE.view.Viewport

Ext.define('BE.view.Viewport', {

    // {{{ requires

    requires: [
        'BE.view.Usergrid'
    ],

    // }}}
    // {{{ extend

    extend: 'Ext.container.Viewport',

    // }}}
    // {{{ layout

    layout: {
        type: 'border',
        padding: 5
    },

    // }}}
    // {{{ items

    items: [{

        region: 'center',
        //html: 'hoge'
        xtype: 'usergrid'

    }]

    // }}}

});

// }}}

Controllerの設置

上記のcontrollersをコメントインして(最初はコメントアウトしてhogeだけ表示させてた)Main,Usergridのコントローラを作成。
* MainControllerの設置をします。
* UsergridControllerの設置をします。
* これもスニペットで作成。設置のみ。ほとんどコメントアウト状態。以下、例。

  • Main.js
メインコントローラはコンポーネントからの独自イベントをディスパッチするように しておきます。
// {{{ 'BE.controller.Main',
/**
 * @class BE.controller.Main
 * メインコントローラ
 */
Ext.define('BE.controller.Main', {
    // {{{ extend

    extend : 'Ext.app.Controller',

    // }}}
    // {{{ refs インスタンス化されたViewへの参照提供 get([R]efname)
    /*
    refs: [{
        selector: 'xtype..',ref: 'localname'
    }],
    */
    /*
    // }}}
    // {{{ views クラスへの参照提供 get(Xxxx)View

    views:[
        'Views'
    ],

    // }}}
    // {{{ models クラスへの参照提供 get(Xxxx)Model

    models:[
        'Models'
    ],

    // }}}
    // {{{ stores インスタンスへの参照提供 get(Xxxx)Store

    stores: [
        'Store'
    ],

    // }}}*/
    // {{{ init

    //アプリケーション起動時にコールされる特別なメソッド
    //ApplicationのLaunch前にコールされる
    init: function() {

        //alert('hoge');
        var me = this;
        me.control(me.bindItem);

    },
    // {{{ bindItem
    bindItem: {

        // 作成
        'usergrid' : {
            create: function(){
                var me = this;
                alert('create dispatch');
                me.getController('Usergrid').onCreate();
            },
            reset: function(){
                var me = this;
                alert('reset dispatch');
                me.getController('Usergrid').onReset();
            },
            save: function(){
                var me = this;
                alert('save dispatch');
                me.getController('Usergrid').onSave();
            },
            del: function(rowIndex){
                var me = this;
                alert('save dispatch');
                me.getController('Usergrid').onDelete(rowIndex);
            }

            /*
            hoge: function(){
                var me = this;
                me.getController('Hoge').onHoge();
            },*/
        }

    }
/*
    // }}}
    // }}}
    // {{{ onXXXX

    onXXXX: function() {
        console.log('hoge');
    }
*/
    // }}}

});
// }}}

  • Usergrid.js
refとか汎用の名前にしたり、onXXとして実装と分離して抽象クラスとして
沈み込ませてそれを継承させたらすっきりするかなぁ。とおもってます。
// {{{ 'BE.controller.Usergrid',
/**
 * @class BE.controller.Usergrid
 * ユーザグリッドコントローラ
 */
Ext.define('BE.controller.Usergrid', {
    // {{{ extend

    extend : 'Ext.app.Controller',

    // }}}
    // {{{ refs インスタンス化されたViewへの参照提供 get([R]efname)

    refs: [{
        selector: 'usergrid',ref: 'crudgrid'
    }],

    // }}}
    // {{{ views クラスへの参照提供 get(Xxxx)View

    views:[
    ],

    // }}}
    // {{{ models クラスへの参照提供 get(Xxxx)Model
    // }}}
    // {{{ stores インスタンスへの参照提供 get(Xxxx)Store

    stores: [
        'User'
    ],

    // }}}
    // {{{ init

    //アプリケーション起動時にコールされる特別なメソッド
    //ApplicationのLaunch前にコールされる
    init: function() {

        var me = this;
        me.control(me.bindItem);

    },

    // }}}
    // {{{ bindItem
    bindItem: {

        // 作成
        'usergrid button[action=create]' : {
            click: function(){
                var me = this;
                alert('create');
                me.getCrudgrid().fireEvent('create');
            }
        },
        // リセット
        'usergrid button[action=reset]' : {
            click: function(){
                var me = this;
                alert('reset');
                me.getCrudgrid().fireEvent('reset');
            }
        },
        // 保存
        'usergrid button[action=save]' : {
            click: function(){
                var me = this;
                alert('save');
                me.getCrudgrid().fireEvent('save');
            }
        },
        // 削除
        'usergrid actioncolumn' : {
            click: function(view,cell,row,col,e){
                var me = this;
                alert('delete');
                me.getCrudgrid().fireEvent('del',row);
            }
        }

    },
    // }}}
    // {{{
    onCreate: function() {
        var me = this;
        me.create();
    },
    onSave: function() {
        var me = this;
        me.save();
    },
    onDelete: function(rowIndex) {
        var me = this;
        me.del(rowIndex);
    },
    onReset: function() {
        var me = this;
        me.reset();
    },
    // }}}
    // {{{
    create: function() { // scope is a config object
        var me = this,
            grid = me.getCrudgrid(),
            edit = grid.getPlugin('cellplugin'),
            store = grid.getStore(),
            model = store.getProxy().getModel(),
            rec;

        // Cancel any active editing.
        edit.cancelEdit();

        // create new entry
        rec = Ext.create(Ext.ClassManager.getName(model),{});

        // add entry
        store.insert(0, rec);

        // start editing
        edit.startEditByPosition({
            row: 0,
            column: 0
        });
    },
    save: function() { // scope is the store
        var me = this,
            valid = true,
            msg = "",
            name,
            grid = me.getCrudgrid(),
            store = grid.getStore();

        // check if all changes are valid
        store.each(function(el) {
            if(!el.isValid()) {
                valid = false;
                name = el.get('name') || el.get('title') || (el.phantom ? "New entry" : el.getId());
                msg += "<br><br><b>"+name+":</b>";
                el.validate().each(function(error) {
                    msg += "<br>&nbsp;&nbsp;&nbsp;"+error.field+" "+error.message;
                });
            }
        });

        if(!valid) {
            Ext.MessageBox.show({
                title: 'Invalid Data',
                msg: '<div style="text-align:left; padding-left:50px;">There are errors in your data:'+msg+"</div>",
                icon: Ext.MessageBox.ERROR,
                buttons: Ext.Msg.OK
            });
        } else {
            // commit create and update
            store.sync();
        }
    },
    del: function (rowIndex) {
        var me = this,
            grid = me.getCrudgrid(),
            store = grid.getStore();
        rec = store.getAt(rowIndex),
        name = Ext.getClassName(rec);

        // instantly remove vom ui
        store.remove(rec);

        // sync to server
        // for before-ExtJS 4.1 the callbacks will be ignored,
        // since they were added in 4.1
        store.sync({
            success: function(record,operation) {

                Ext.MessageBox.show({
                    title: name + ' record deleted',
                    msg: name + ' record was successfully deleted.',
                    icon: Ext.MessageBox.INFO,
                    buttons: Ext.Msg.OK
                });
            },
            failure: function(record,operation) {

                // since it couldn't be deleted, add again
                store.add(rec);

                // inform user
                Ext.MessageBox.show({
                    title: name + ' record could not be deleted',
                    msg: operation.getError() || (name + ' record could not be deleted.'),
                    icon: Ext.MessageBox.ERROR,
                    buttons: Ext.Msg.OK
                });
            }
        }); //eo store sync
    },
    reset: function() { // scope is the store
        // reject all changes
        var me = this,
            grid = me.getCrudgrid(),
            store = grid.getStore();

        store.each(function(rec) {
            if (rec.modified) {
                rec.reject();
            }
            if(rec.phantom) {
                store.remove(rec);
            }
        });
    }
});
// }}}

storeの設置

ストアの設置です。 で、いままでProxyの設定はStoreでやってたんですけど、、 Banchaが提供するModelにはすでにCakeにあわせたProxyの設定が 盛り込まれたModelを返却するのでストアはBanchaの提供するモデルを 設置するのみです。
// {{{ 'BE.store.User',
/**
 * @class BE.store.User
 * desctription
 */
Ext.define('BE.store.User', {
    // {{{ extend

    extend : 'Ext.data.Store',

    // }}}
    // {{{ model

    model: Bancha.getModel('User'),
    autoLoad: true

    // }}}

});
// }}}

modelの設置

モデルは要らないっす。 ModelはBanchaが提供してくれますので。 Bancha.getModel(‘モデル名’); で取得可能です。
または、Bancha.model.モデル名ですかね。。。

これで全部載せたかな。。。

all.jsからの返却

rpc用のメソッド一覧等の情報がちゃんとできているか見てみます。 all.jsのレスポスンスをJavaScriptBeautifierで整形してみました。 よさそうです。
Ext.ns('Bancha');
Bancha.REMOTE_API = {
    "url": "\/~banchatest\/bancha.php",
    "namespace": "Bancha.RemoteStubs",
    "type": "remoting",
    "metadata": {
        "User": {
            "idProperty": "id",
            "fields": [{
                "name": "id",
                "type": "int"
            }, {
                "name": "username",
                "type": "string"
            }, {
                "name": "password",
                "type": "string"
            }, {
                "name": "first_name",
                "type": "string"
            }, {
                "name": "last_name",
                "type": "string"
            }, {
                "name": "created",
                "type": "date",
                "dateFormat": "Y-m-d H:i:s"
            }, {
                "name": "modified",
                "type": "date",
                "dateFormat": "Y-m-d H:i:s"
            }],
            "validations": [],
            "associations": [],
            "sorters": []
            },
        "_UID": "5030537f74c0a675541393"
    },
    "actions": {
        "User": [{
            "name": "getAll",
            "len": 0
        }, {
            "name": "read",
            "len": 1
        }, {
            "name": "create",
            "len": 1
        }, {
            "name": "update",
            "len": 1
        }, {
            "name": "destroy",
            "len": 1
        }, {
            "name": "submit",
            "len": 1,
            "formHandler": true
        }],
        "Bancha": [{
            "name": "loadMetaData",
            "len": 1
        }]
        }
}
※注目すべきはxframeworkでもハマった
"formHandler": true
これ。これは.formのAPIに設定する用のハンドラにはこれをつけます。 xframeworkの時はドキュメントコメントに記載すると フレームワークがパースしてこのフラグを立ててくれてました。 超ハマったのでよく覚えてます。

実行してみる。





まとめ

で、、 プロトタイプを作成しよう!となると、
  1. UserDirの設置
  2. cake2.1.5の設置
  3. htaccessの変更
  4. Bancha0.9.5の設置(cake配下のPlugin下)
  5. bake実施(モデルとコントローラ)
  6. bakeで生成したモデルに追記
    public $actsAs = array(‘Bancha.BanchaRemotable’);
  7. Extjsの設置(cake配下app/webroot/js/配下)
  8. ExtJsMVCの設置
こんなかんじかな。
cake→banchaと夜な夜な帰宅後にやったわりには理解は思ったより早くてすっきりしてきた。 これを業務にフィードバックできればいいんだけど、
あとは、banchascaffoldだね。

感想

久々のMVCの設置、スニペットがあるのと無いのでは雲泥の差だし、
何より先の技術サイトのように一気通貫で説明があると助かるなぁ。。
と思いこのエントリも自分用に再度まとめ直したし、サンプルも作り替えた。
疲れた。。。

2012年8月5日日曜日

banchaを触ってみた3

概要の概要

出来上がったコントローラ見ると、 リクエスト情報の内、isBaanchaとかをみてSaveAndXXXとか 呼び出している。。

のはいいのだけど、

最初のおおまかな流れがつかめない。 気になるのがRPC用のメソッド一覧の取得契機。

ほんの少しだけ見てみた。 もうほんとこまかなロジックはすっ飛ばして 「どこでやってんのかしらね。。」 程度です。

いつものaddProviderはどこ?

まず、 Plugin/Bancha/webroot/js/Bancha.js

initメソッドで

Ext.direct.Manager.addProvider()

しているところがありました。

で、こいつに渡しているのがメソッド一覧のオブジェクトになるから、 上を追うとgetRemoteApiがある これはthis.remoteapi(Bancha.REMOTEAPI)となる。 このBancha.REMOTEAPIを事前にリクエストしておくひつようがあるわけで。

initはonModelReadyでExt.OnReadyをコールし、そのなかでinitとされている。

つまり

Bancha.onModelReady => Ext.OnReady => init => addProvider

となることはわかった。

Bancha.REMOTE_APIはどこで生成?

上記で事前に取得しておくBancha.REMOTEAPIについてざっと見てみる。 そもそもこのBancha.REMOTEAPIという命名はWikiにもあるように Config設定にある(grep結果)

/UserDir/banchatest/public_html/app/Plugin/Bancha/Config/bootstrap.php|23 col 30| Configure::write('Bancha.Api.remoteApiNamespace','Bancha.REMOTE_API');

で、この設定が利用されているのは

/app/Plugin/Bancha/Controller/BanchaController.php|86 col 78| $this->response->body(sprintf("Ext.ns('Bancha');\n%s=%s", Configure::read('Bancha.Api.remoteApiNamespace'), json_encode($api)));

のこの部分。

で/app/Plugin/Bancha/Lib/Bancha/BanchaApi.php でモデル一覧取得して、 モデル名を引数にコントローラへアクセスしてCrud用のAction名を取得している。

このあたりはCake特有の命名規則でルーティングしてクラス名をつくりだしたりしているので 深追いせずに次行っちゃいます。

つまり、

モデル名一覧 => モデル名からコントローラ => コントローラのCrud用メソッド一覧。

それを連想配列でredirect。

Ext.ns('Bancha'); Bancha.REMOTEAPI = jsonencodeした上記連想配列。

をredirect->bodyしているわけですね。

概要だけでも少し追うと安心できました。

そのBancha.REMOTE_APIはいつのリクエストで生成?

で、 このBancha.REMOTE_APIは、じゃぁ、いつのリクエストで生成されて ロードされているか。となるんだけど、、、 これは、bancha-api.jsを呼んで、 Banchaのルーティング設定でBanchaControllerのindexアクションが呼び出される 設定になってるっぽい(routes.php)。。(これはちょっと迷子になった。)

hoge.html内で

<!-- include Bancha and the remote API -->
<script type="text/javascript" src="Bancha/js/Bancha.js"></script>

※ここでBancha利用用のBanchaクラス群ロード


<script type="text/javascript" src="bancha-api.js"></script>

※これでサーバサイドでルーティングされてBanchaControllerのindexアクションコール
  返却でurl(RPC時のルーティングするurl)やメソッド一覧のJSON返却

このあとは各App用JSでOnModelReadyを実装してそのなかでaddProvider
ということはこのコールバック内に今までのエントリポイントとなる
メソッドを設置することになるでしょうかね。。

次は疎通。疎通~。

現状のフォルダ構成

▾ banchatest/
  ▾ public_html/
    ▾ app/
      ▸ Config/
      ▸ Console/
      ▸ Controller/
      ▸ Lib/
      ▸ Locale/
      ▸ Model/
      ▾ Plugin/
        ▾ Bancha/
          ▸ _app/
          ▾ Config/
              bancha-dispatcher-bootstrap.php
              bootstrap.php
              routes.php・・・・・・・・ここ
          ▸ Console/
          ▾ Controller/
              BanchaAppController.php
              BanchaController.php・・・・・・・・ここ
          ▾ Lib/
            ▾ Bancha/
              ▸ Exception/
              ▸ ExceptionHandler/
              ▸ Network/
              ▸ Routing/
                BanchaApi.php・・・・・・・・ここ
          ▸ Model/
          ▸ Test/
          ▾ webroot/
            ▾ js/
                Bancha.js*・・・・・・・・ここ
              setup-check.html
            .gitignore
            gpl-v3.txt
            license.txt
            README.md
            UpdateFromBeta3.txt
            VERSION.properties*
          empty*
      ▸ Test/
      ▸ tmp/
      ▸ Vendor/
      ▸ View/
      ▸ webroot/
        .htaccess*
        index.php*
    ▸ lib/
    ▾ plugins/
        empty*
    ▸ vendors/
      .gitignore*
      .htaccess*
      .travis.yml*
      build.properties*
      build.xml*
      index.php*
      README*

banchaを触ってみた2


bakeでBancha用のモデルとコントローラの作成

bakeで自動生成します。 まず、カレントディレクトリをappにして

cd UserDir/public_html/app

./Console/cake bake

を実行。

DB設定

Welcome to CakePHP v2.2.0 Console
---------------------------------------------------------------
App : app
Path: /Volumes/DataDrive/UserDir/banchatest2/public_html/app/
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> D
---------------------------------------------------------------
Database Configuration:
---------------------------------------------------------------
Name:  
[default] > 
Datasource: (Mysql/Postgres/Sqlite/Sqlserver) 
[Mysql] > 
Persistent Connection? (y/n) 
[n] > 
Database Host:  
[localhost] > 
Port?  
[n] > 
User:  
[root] > web
Password:  
> ************************ 
Database Name:  
[cake] > tasks
Table Prefix?  
[n] > 
Table encoding?  
[n] > UTF8

---------------------------------------------------------------
The following database configuration will be created:
---------------------------------------------------------------
Name:         default
Datasource:       Mysql
Persistent:   false
Host:         localhost
User:         web
Pass:         ******
Database:     tasks
Encoding:     UTF8
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y
Do you wish to add another database configuration?  
[n] > n

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Config/database.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Config/database.php`

次にモデル。

Welcome to CakePHP v2.2.0 Console
---------------------------------------------------------------
App : app
Path: /Users/froggugugugu/UserDir/cakephpTest/public_html/app/
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> M
---------------------------------------------------------------
Bake Model
Path: /Users/froggugugugu/UserDir/cakephpTest/public_html/app/Model/
---------------------------------------------------------------
Possible Models based on your current database:
1. TblPlan
2. TblProject
3. TblUser
Enter a number from the list above,
type in the name of another model, or 'q' to exit  
[q] > 3
What is the primaryKey?  
[Id] > 

.....略..................

.....途中でBancha用のテンプレート選択..................

Associations:
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y
---------------------------------------------------------------
You have more than one set of templates installed.
Please choose the template set you wish to use:
---------------------------------------------------------------
1. bancha
2. default
Which bake theme would you like to use? (1/2) 
[1] > 1

Baking model class for TblUser...

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Model/TblUser.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Model/TblUser.php`

で、モデル出来上がり。

次にコントローラ

What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> C
---------------------------------------------------------------
Bake Controller
Path: /Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/
---------------------------------------------------------------
Possible Controllers based on your current database:
---------------------------------------------------------------
 1. TblPlans
 2. TblProjects
 3. TblUsers
Enter a number from the list above,
type in the name of another controller, or 'q' to exit  
[q] > 3
---------------------------------------------------------------
Baking TblUsersController
---------------------------------------------------------------
Would you like to build your controller interactively? (y/n) 
[y] > 
Would you like to use dynamic scaffolding? (y/n) 
[n] > 
Would you like to create some basic class methods 
(index(), add(), view(), edit())? (y/n) 
[n] > y
Would you like to create the basic class methods for admin routing? (y/n) 
[n] > 
Would you like this controller to use other helpers
besides HtmlHelper and FormHelper? (y/n) 
[n] > 
Would you like this controller to use any components? (y/n) 
[n] > 
Would you like to use Session flash messages? (y/n) 
[y] > 

---------------------------------------------------------------
The following controller will be created:
---------------------------------------------------------------
Controller Name:
    TblUsers
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y

Baking controller class for TblUsers...

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/TblUsersController.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/TblUsersController.php`

ひとまず完了

さっそく出来上がったものを見て見ます。

banchaを触ってみた


banchaを使ってみる

banchaのサイトは

こちらから

インストールから

まずはCakeの設置。

(bancha:0.9.5はcake2.1.X(現在は2.1.5)に対応しています。
Dispacherの実装が2.2.Xだとことなるため、BanchaのDispatcherの実装が合いません::つまづきました。。)

設置といってもUserDirに配置して

Config/database.php

を適宜に設定。 ユーザディレクトリ利用しているのであれば、

public_html下の.htaccessに

RewriteBase    /~banchatest/

public_html/app下の.htaccessに

RewriteBase    /~banchatest/app/

public_html/app/webrot下の.htaccessに

RewriteBase    /~banchatest/app/webroot/

を追記してひとまず完了。

Banchaの設置

ダウンロードしてきたフォルダをそのまま

public_html/app/Plugin/Bancha

として設置。

bootstrap.phpに追記

public_html/app/Config/bootstrap.phpに

CakePlugin::load(array('Bancha' => array('routes' => true, 'bootstrap' => true))); 

を追記。

bancha.phpファイルコピー

/Users/froggugugugu/UserDir/banchatest/public_html/app/Plugin/Bancha/_app/webroot/bancha.php

/Users/froggugugugu/UserDir/banchatest/public_html/app/webroot/bancha.php

にコピー。

シンボリックリンクってのもあるんだろうけど、、、

$ sudo ln -s /Users/froggugugugu/UserDir/banchatest/public_html/app/Plugin/Bancha/_app/webroot/bancha.php /Users/froggugugugu/UserDir/banchatest/public_html/app/webroot/bancha.php

これだと内部のファイルパス取得部分でPlugin配下を参照するようになってる?? みたいだったのでもうやめてファイルコピーで対応。

public_html/app/Config/core.php

末尾に

/**
 * Configure the cache for Banchas Remote API.
 */
Cache::config('_bancha_api_', array(
    'engine' => $engine,
    'prefix' => $prefix . 'bancha_api_',
    'path' => CACHE . 'models' . DS,
    'serialize' => ($engine === 'File'),
    'duration' => $duration
));

を追記。

設置できたかの確認。

http://localhost/~banchatest/Bancha/setup-check.html

OK..

Cakeでつかう(Bakeでひな形作成)

Bachaでクライアントサイドに公開する方法として モデルの保有するメソッドの公開とコントローラが保有するメソッドの 公開があるんだけど、一般的にはモデル公開。 このあたりはxFrameworkPXでもやったことあるので理解は楽。

モデルの公開方法

bakeで用意します。 こちら

cakeを触ってみた2

前回の続き。

CakePHP2.0

こちらををベースに写経していきます。

Viewについて

ビューはctpという拡張子で
Viewフォルダ配下にアプリケーション名のフォルダを作成して
その配下に、アクション名のctpファイルを作ればいいんだろう。

でデータのバインドは
コントローラ側で
$this->set(‘変数名’,変数);
という具合でできる。

ビュー側では
そいつを<?php echo $変数名 ?>とかで出力する。

次。
モデルに行きます。

DB

MySQLでDB作ります。
よくあるtbl_Userとかのユーザテーブルにしました。
CakeではXXXdatasっていうテーブル名にするみたい。
でもマスタはmstリソース、イベント系はtblとかに
する事が多いのでここはtbl_Userとしておきます。

と思いきや、
cakeの特有の命名規約があるみたい。
テーブルにはsがついた複数形として
モデルはsがつかない。
というルールにしておけばよさそう。。

tbl_Users

とすれば

TblUser

というモデル名となります。
※このあとでfind(‘list’);ってやった時にフィールド名にデフォルトのモデル名が利用されてSQLエラーが
発生したけど、エラー情報からオブジェクトにaliasを設定すればよさそうなことがわかり、対応。

  • controller = 機能要件に沿ったアプリケーション名として
  • model ≒ DBテーブル名 ≒ 実世界のデータ・モデル名に近い命名

DB接続情報

/app/Config/.database.php.defaultがあるので  
/app/Config/.database.php
としてコピーして利用します。 で、$testは今のところ利用しないので削除しちゃいます。 ひとまず勉強のためにやってますが、これはあとでbakeで対話式に 設定可能。

modelについて

DBの説明でもあったように命名に癖がある。

クラス定義

Controllerクラスの定義はまず、

App::uses('AppController','Controller');  

として利用できるように定義、
AppControllerクラスを継承して定義するみたい。

これも結局あとでbakeで生成されるけど、いちどはやっとかんとね。

ひとまず、写経してどのフォルダに何入れるか。。
ってのと命名規則のさわりだけはOKとした。

時間ないのでCakeについてはここまで。

次はBancha。

2012年8月1日水曜日

cakeを触ってみた

先日、cakeを試してみようとおもってて、、
でもExtJSとの絡みを考えるとDirect使えんしなぁ
と呟いてたら@martini3ozさんがBanchaってのがそれっぽいよ。
と教えてくれました。
見てみます!と返信したもののCake自体が
チンプンカンプンなので入門の入門を触ってみました。

CakePHP2.0

事始め
こちらををベースに写経していきます。

ダウンロード~動かすまで

  1. DL
    2012/7/6現在の最新版のstableである2.2.0を使用してみます。
    こちらのTOP右側のリンクからDL可能。

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
前のエントリでも書きましたが、Bancha0.9.5は2.2.Xシリーズには
対応しておらんようです。
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

  1. 設置
    UserDir有効のためUsrDir/cakephpTest/public_html配下にcakephpTestとして配置してみた。
    で、このままだとnotfoundになるのでpublick_html配下の.htaccessに

    RewriteBase /~cakephpTest/

必然的にその配下の関連する.htaccessも書きかえました。

public_html/app配下の.htaccess

RewriteBase /~cakephpTest/app/  

public_html/app/webroot配下の.htaccess

RewriteBase /~cakephpTest/app/webroot/  

を追記。

  1. 確認

    localhost:8080/~cakephpTest/

アクセスして確認。
* DBの設定
* セキュリティソルト、セキュリティシードはあと。で設定。

Controllerについて

コントローラは/app/Controller配下に作る。
コントローラは(アプリケーション)ごとに設置と理解。
で、コントローラ名は(アプリケーション名:パスカルケースにして)+Controller.phpとするらしい。
ex)hogehogeというアプリケーション→HogehogeController.php
コントローラ(アプリケーション)は複数のアクションをまとめたもの?として理解。

ファイル命名規則は、パスカルケースで、

XxxxxxController.php  

として作成すること。
クラスの命名規則も同様に。

クラス定義

Controllerクラスの定義はまず、

App::uses('AppController','Controller');  

として利用できるように定義、
AppControllerクラスを継承して定義するみたい。

アクション定義

publicメソッドがアクションとして認識されるようですね。
メソッド≒アクション。と。
基本、アクションが1URLに割り当てられる見たい。

アクション間遷移

  • リダイレクト

    public function index()
    {
    $this->autoRender = false;
    $this->redirect(‘./other’);/そのコントローラ(アプリケーション)内のotherメソッド(アクション)にリダイレクト/
    }

  • フォワード
    public function index()
    {
    $this->autoRender = false;
    $this->setAction(‘other’);/そのコントローラ(アプリケーション)内のotherメソッド(アクション)にフォワード(URLの変更は発生せず)/
    }

  • URLパラメタによる値渡し

    $this->redirect(‘./other/hoge/fuga/piyo’);

Modelの利用

モデル名がコントローラ名と一致した際に自動的に利用可能になるようです。
ただ、明示的に設定することも可能で、
var $uses = array(‘Recipe’, ‘User’);
のように$usesプロパティに指定することでインスタンス化されるようです。


お試しコード

<?php  

App::uses('AppController', 'Controller');  

// {{{ sampleController  
/**  
 * sampleController Class  
 *  
 * sample  
 *  
 * @category   CategoryName  
 * @package    PackageName  
 * @copyright  2011-2011  
 * @license    http://www.opensource.org/licenses/mit-license.html MIT License  
 * @version    Release: sample  
 */  
class SampleController  
extends AppController  
{  
    // {{{ const  
    // }}}  
        // {{{ props  
    // }}}  
    // {{{ __construct  
    /**  
     * コンストラクタ  
     *  
     * @param type argname discription  
     **/  
//    function __construct($argname)  
//    {  
//        # code...  
//    }  
//    // }}}  
//    // {{{ __destruct  
//    /**  
//     * デストラクタ  
//     **/  
//    function __destruct()  
//    {  
//        # code...  
//    }  
    // }}}  
    // {{{ index  
    /**  
    *  
    *  
    * @param type $argname discription  
    * @return type discription  
    */  
    public function index()  
    {  
        $this->autoRender = false;  
        //var_dump('index');  
        //$this->redirect('./other/hoge/fuga/piyo');  
        //$this->setAction('other','hoge','fuga','piyo');  
        $params = array(  
            'hoge' => array(  
                'hoge1' => 'hogehoge1',  
                'hoge2' => 'hogehoge2',  
                'hoge3' => 'hogehoge3',  
            ),  
            'fuga' => array(  
                'fuga1' => 'fugafuga1',  
                'fuga2' => 'fugafuga2',  
                'fuga3' => 'fugafuga3',  
            )  

        );  
        //$this->setAction('other','hoge','fuga','piyo');  
        $this->setAction('other',$params);  
    }  

    //public function other($a,$b,$c){  
    public function other($params){  
        $this->autoRender = false;  
        //var_dump('other');  
        //var_dump($a);  
        //var_dump($b);  
        //var_dump($c);  
        var_dump($params);  
    }  

    // }}}  
}  
// }}}  

2012年7月29日日曜日

Bancha0.9.5 はCake2.1.5を食べるときにのむ。2.2.Xではない。

warningが出てるparseParamsでおきる+うまくいかない。Viewが無いとか言ってうまくいかない。。

(Cakeのバージョンがちがうよこれ。:後日談)


(後日追記):
CakeのDispatcherの実装がかなりかわってて
Banchaがそれにあわせて実装してあったからそもそも
うまく動きませんでした。よ。。。っていうオチです。

2.1.X系はDispatcherでinvoke呼び出しているからBanchaのinvokeが
呼び出されてバンザイなんだけど、

2.2.X系は_invoke呼び出されないし、それ以前にいろんなところで
関数のIF違うわなんだで動きません。

以下はリーディングした際のログ。
結局どうこういう以前のバージョン確認しろよお前。という
どうしようもなく目も当てられないミスを犯したのですが。。


疎通させている時の話

うまくいきません。うーまーくーいーかーなーいーよー。
帰宅後の僅かな時間しか取れない自分にとってほんのちょっとの疎通障害は
もうかなりきついのであーだこーだ言う前に急がば回れということで
ざっとソースをみてみます。。

warning発生箇所はBanchaSingleDispatchのparseParamsで第一引数はCakeRequestじゃなきゃダメだ。
みたいなこと言われてるっぽい。

perseParamのWarningは。。。

$dispatcher->dispatch($request, new CakeResponse(array('charset' => Configure::read('App.encoding'))), array('return' => true)), // second argument is expected to be overwritten by BanchaSingleDisptacher::_invoke

この第三引数のreturn=>trueが重要。
これは最後の方にBanchaSingleDispatchの_invokeで番茶用にカスタマイズしたロジックを
通らなくなってしまう。。
このarray(‘return’ => true)をrequest->param[‘return’]として追加する機構が
perseParamで行なっている

$request->addParams($additionalParams);

となります。。
さて、、、
ダンプしてみるとCakeEventがわたってきてる。
(これが例外のWarningの原因)

perseParamを追って見ることにする

grepしてみるとCakeのDispatcherのimplementedEventsに

return array('Dispatcher.beforeDispatch' => 'parseParams');

のようにあって、イベント名とメソッド名を結びつけているような実装がある。
もう一つはその設定のイベントを生成してコールしている部分ぽい実装として
BanchaDispatch::dispatch内の実装がある。
このBanchaDispatch::dispatchはbancha.phpから
呼び出されるBanchaの最初の関数(今後追加するブログのBanchaを使うところでも言っているけど)。

BanchaDispatch::dispatch内はというと、、

BanchaSingleDispach::がインスタンス化されて、そのBachaSingleDispatchのdispatchをしている。
BanchaSingleDispachはCakeのDispacherを継承しているので実際に呼ばれるdispacherはcakeのDispatcherになる。
基底Dispachでイベントが生成される(Dispatcher.beforeDispatch)。

(BanchaDispatch)  
public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = array()) {  
    $beforeEvent = new CakeEvent('Dispatcher.beforeDispatch', $this, compact('request', 'response', 'additionalParams'));  

こんな具合で。。
ここで
CakeEventを生成してて、
ここに引き渡すのはそれぞれ、CakeEventクラスのメンバname、subject、dataに格納されるようです。。

CakeEvent->data = array($requestと$responseと$additionalParamsの配列)になる。。  

$beforeEventはEventManagerによってディスパッチされています。
イベント名で紐付けて呼び出されるみたい。。

(Dispach)  
$this->getEventManager()->dispatch($beforeEvent);  

EventManagerを掘り下げてみる。

このgetEventManager()はCakeEventManagerをインスタンス化して、
CakeEventManager->attach($this)になる。。。
この際の$thisはBanchaSingleDispatchになる。。。
BanchaSingleDispatch->Dispache->CakeEventListenerの継承関係だから。
attachのなかのif制御でinstanceof CakeEventListenerのになる
で、

(CakeEventManager->attach内)  
public function attach($callable, $eventKey = null, $options = array()) {  
   .....  
   $this->_attachSubscriber($callable);  

これが呼び出される。。callableはBanchaSingleDispatch。

attachSubscriberが呼び出される

この_attachSubscriber…は

(CakeEventManager->_attachSubscriber)  
protected function _attachSubscriber(CakeEventListener $subscriber) {  
    foreach ($subscriber->implementedEvents() as $eventKey => $function) {  
        $options = array();  
        $method = $function;  
        if (is_array($function) && isset($function['callable'])) {  
            list($method, $options) = $this->_extractCallable($function, $subscriber);  
        } elseif (is_array($function) && is_numeric(key($function))) {  
            foreach ($function as $f) {  
                list($method, $options) = $this->_extractCallable($f, $subscriber);  
                $this->attach($method, $eventKey, $options);  
            }  
            continue;  
        }  
        if (is_string($method)) {  
            $method = array($subscriber, $function);  
        }  
        $this->attach($method, $eventKey, $options);  
    }  
}  

となってて、implementedEventsは継承元のDispatcherとなるから
eventKey = ‘Dispatcher.beforeDispatch'
function = 'parseParams'
となる。

と、、
functionは文字列なので

$method = array( BanchaSingleDispatch, 'parseParams' )  

ということになる。
次にattachが呼び出されている。。。

attachが呼び出される

$this->attach($method, $eventKey, $options);  

この引数はそれぞれ、

$method = array( BanchaSingleDispatch, 'parseParams' )  
$eventKey = 'Dispatcher.beforeDispatch'  
$options = array()  

となる。。

(CakeEventManager->attach)  
   public function attach($callable, $eventKey = null, $options = array()) {  
    if (!$eventKey && !($callable instanceof CakeEventListener)) {  
        throw new InvalidArgumentException(__d('cake_dev', 'The eventKey variable is required'));  
    }  
    if ($callable instanceof CakeEventListener) {  
        $this->_attachSubscriber($callable);  
        return;  
    }  
    $options = $options + array('priority' => self::$defaultPriority, 'passParams' => false);  
    $this->_listeners[$eventKey][$options['priority']][] = array(  
        'callable' => $callable,  
        'passParams' => $options['passParams'],  
    );  
}  

listenerに登録される

ここでCakeEventManagerの_listenersに

['Dispatcher.beforeDispatch'][優先度][] = array('callable' => array(BanchaSingleDispatch,'parseParams'),'passParams'=>false)  

として登録される。
これでCakeEventMangerに登録される仕組みがわかりました。

今度は呼び出し側を追ってみます。

このdispatchの内容は
(CakeEventManager->dispatch内)

foreach ($this->listeners($event->name()) as $listener) {  
    if ($event->isStopped()) {  
        break;  
    }  
    if ($listener['passParams'] === true) {  
        $result = call_user_func_array($listener['callable'], $event->data);  
    } else {  
        $result = call_user_func($listener['callable'], $event);  
    }  
    if ($result === false) {  
        $event->stopPropagation();  
    }  
    if ($result !== null) {  
        $event->result = $result;  
    }  
    continue;  
}  

となってて、気持ちとしては'passParams === trueの制御が効いてほしいんだけど、
前述したように、_listenersにはpassParamsがfalseで登録されているから、
elseのほうに行って

$result = call_user_func($listener['callable'], $event);  

が呼び出されて、parseParamsの引数はCakeEventとしてよびだされているのではないか。

すこしだけ。Banchaを変更してみる。試しにperseParamをCakeEventを受け取る形のものを実装してみます。。

public function parseParams(CakeEvent $event) {  
    if (!empty($event->data['additionalParams'])) {  
        $event->data['request']->addParams($event->data['$additionalParams']);  
    }  
    return $request;  
}  

これだけど、そもそもCakeのDispatchの制御がそもそも違う。

Cake2.1.0だと
dispatchでthis->_invokeとコールされていて、そのさきはBanchaSingleDispatcherとなって
そのなかでreturnした結果をそのままdispatcherの戻り値として返却するからdispatcherとしての
制御ってそこで終わってた。。

Cake2.2.1だと
そもそも_invokeすら呼び出されていない氏ね。

で、ようやっとひと通りの大雑把な流れはつかめた

あれ?詰んだらしい。www
そもそもイベントリスナへ登録する時passParam =がfalseとして登録されてて、
途中どこにも更新される点がない点で詰んでる。

あれ?これはおかしくないかということで過去のソースを追ってみると
あ、あ、あ、あ、あ、あ
これならBanchaの_invokeも動くわな。。という実装。2.1.5で確認してみました。

このあたりの動きは そもそもこれはCakeの実装に依存している箇所だからね。
Cakeのバージョン2.1.0だとテストしたということ書いてあったけどね。
そりゃ2.2系はまだか。

お陰でBanchaと少し友だちになれましたww

ざんねん。
貴重な勉強時間がごっそりと削られました(まぁ、これも勉強になりましたが)。

2012年7月28日土曜日

JSDuckを触ってみた

JSDuckを利用してみる。

FaceBookで@martini3ozさんがシェアしてて
ググったら@kotsutsumiさんがTwitterで
質問に答えてて、
結果、こちらにお世話になりました。
さて何からすればいいのか。という取っ掛かりの部分、
とても助かりました。ありがとうございました。
チュートリアルを実施してみたので ちょっと残しておきます。

目指すゴール

左にメニューがあって 内容が切り替わる。と、 で、内容は都度MarkDownで作成できて任意に増やせればOK。

インストール

$ sudo gem install jsduck

ヘルプを見てみる。–helpで引いたら=fullつければ全て見れるってから全て見てみた。

$ jsduck --help=full                                                                                                                                                        12-06-18
Usage: jsduck [options] files/dirs...

-o, --output=PATH                Directory to output all this amazing documentation.
                                 This option MUST be specified (unless --stdout).
                                 Use dash '-' to write docs to STDOUT (only export).

    --ignore-global              Turns off the creation of global class.

    --external=Foo,Bar,Baz       Declares list of external classes.  These classes
                                 will then not generate warnings when used in type
                                 definitions or inherited from.

    --builtin-classes            Includes docs for JavaScript builtin classes.

    --meta-tags=PATH             Path to Ruby file or directory with custom
                                 meta-tag implementations.

    --encoding=NAME              Input encoding (defaults to UTF-8).

-v, --verbose                    This will fill up your console.

Customizing output:

    --title=TEXT                 Custom title text for the documentation.
                                 Defaults to 'Sencha Docs - Ext JS'

    --footer=TEXT                Custom footer text for the documentation.
                                 Defaults to: 'Generated with JSDuck {VERSION}.'

    --head-html=HTML             HTML to append to the <head> section of index.html.

    --body-html=HTML             HTML to append to the <body> section index.html.

    --welcome=PATH               Path to HTML file with content for welcome page.

    --guides=PATH                Path to JSON file describing the guides. The file
                                 should be in a dir containing the actual guides.
                                 A guide is a dir containing README.md, icon.png,
                                 and other images referenced by the README.md file.

    --videos=PATH                Path to JSON file describing the videos.

    --examples=PATH              Path JSON file describing the examples.

    --categories=PATH            Path to JSON file which defines categories for classes.

    --no-source                  Turns off the output of source files.

    --pretty-json                Turn on pretty-printing of JSON.

    --images=PATH                Search path for including images referenced by
                                 {@img} tag. Several paths can be specified by
                                 using the option multiple times.

    --link=TPL                   HTML template for replacing {@link}.
                                 Possible placeholders:
                                 %c - full class name (e.g. 'Ext.Panel')
                                 %m - class member name prefixed with member type
                                      (e.g. 'method-urlEncode')
                                 %# - inserts '#' if member name present
                                 %- - inserts '-' if member name present
                                 %a - anchor text for link
                                 Default is: '<a href="#!/api/%c%-%m" rel="%c%-%m" class="docClass">%a</a>'

    --img=TPL                    HTML template for replacing {@img}.
                                 Possible placeholders:
                                 %u - URL from @img tag (e.g. 'some/path.png')
                                 %a - alt text for image
                                 Default is: '<p><img src="%u" alt="%a"></p>'

    --export=TYPE                Exports docs in JSON.  TYPE is one of:
                                 * full     - full class docs.
                                 * api      - only class- and member names.
                                 * examples - extracts inline examples from classes.

    --seo                        Creates index.php that handles search engine traffic.

    --eg-iframe=PATH             An HTML file to use inside an iframe
                                 to display inline examples.

    --examples-base-url=URL      Base URL for examples with relative URL-s.

    --tests                      Creates page for testing inline examples.

    --stats                      Creates page with all kinds of statistics. Experimental!
と出力されます。

フォルダの配置を決めよう

作業用ディレクトリ作成
~/UserDir/jsducktest
としてみた。
出力用ディレクトリ指定
~/UserDir/jsducktest/release
としてみた。

テンプレートとなるhtmlを作成してみよう

index.htmlを作成して、下記を記載。
<h1>HELLO WORLD h1</h1>
<h2>HELLO WORLD h2</h2>
<h3>HELLO WORLD h3</h3>
<h4>HELLO WORLD h4</h4>
<h5>HELLO WORLD h5</h5>
<h6>HELLO WORLD h6</h6>
たったこれだけとしておく。
現在の状況
▾ jsducktest/
  ▸ release/
    .DS_Store
    index.html

※releaseはフォルダのみindex.htmlを作成してしてみる

ビルドしてみる。

  • 早速ビルドです。
    $jsduck -o ~/UserDir/jsducktest/release –welcome=~/UserDir/jsducktest/index.html
    ※パスはわざとフルパス指定にしてます。 ※作業用ディレクトリに移動してやると省略できてよいです。

できたらアクセス。

release配下のindex.htmlにブラウザからアクセスしてみます。
こんな感じになりました。

index.html(welcomeページのみ)

次に左のメニュー。

  • 作業用ディレクトリに「guides」ディレクトリを作成
    このディレクトリ配下のサブディレクトリごとにページを作るイメージです。 ですから、、、
「guides」にサブディレクトリを作成
このディレクトリ配下にREADME.md(これが本文になります)作ってあとでビルドして
リンクさせます。
ですので、subdir1,とsubdir2を作ってみました。 その中にREADME.mdを作っておきます。もちろんMarkDownで書いておきます。
現在の状況
▾ jsducktest/
  ▾ guides/
    ▾ subdir1/
        icon-lg.png
        README.md
        sample.png
    ▾ subdir2/
        README.md
  ▸ release/
    .DS_Store
    index.html
ツリーを作ります。
これはTreePanel用のオブジェクトリテラルを書いてあげるんですね。 作業用ディレクトリ直下にguides.jsonファイルを作って、、、下記のような内容で記載。
(guides.json)
[
    {
        "title": "RootNode",
        "items": [{
                "name": "subdir1",
                "title": "サブディレクトリ1",
                "description": "解説1"
            },{
                "name": "subdir2",
                "title": "サブディレクトリ2",
                "description": "解説2"
        }]
    }
]

ビルドしてみる(オプションを別ファイルにしてみる)

早速ビルドです。今度はヘルプにのっている様に、他のパラメタも。 たくさんのオプションが増えてきたので別ファイルで記述してみます。 json形式でオプションを設定して–configで設定します。
(jsduck.jsonの内容:ファイル名は任意)
{
    "--title": "jsducktest!!!",
    "--guides": "guides.json",
    "--welcome": "index.html"
    "--footer": "footer!!!",
    "--head-html": "head",
    "--body-html": "body"
}
ビルドしてみる
jsduck -o release --config=jsduck.json 

できたらアクセス。

release配下のindex.htmlにブラウザからアクセスしてみます。
こんな感じになりました。



色々と試してみました。

(jsduck.jsonファイル名は任意)
{
    "--title": "jsducktest!!!",
    "--guides": "guides.json",
    "--videos":"videos.json",
    "--categories":"categories.json",
    "--welcome": "index.html",
    "--footer": "footer!!!",
    "--head-html":"head",
    "--body-html":"body",
    "--warnings": ["-link", "-no_doc"],
    "--":[
        "src/Hoge/AbstructSample.js",
        "src/sample.js"
    ]
}

jsduck -o release --config=jsduck.json
現在の状況
▾ jsducktest/
  ▾ categories/
    ▸ subdir1/
    ▸ subdir2/
  ▾ guides/
    ▸ subdir1/
    ▸ subdir2/
  ▾ release/
    ▸ extjs/
    ▸ guides/
    ▸ output/
    ▸ resources/
    ▸ source/
      app.js
      data.js
      eg-iframe.html
      favicon.ico
      index.html
  ▾ src/
    ▸ Hoge/
      sample.js
  ▾ videos/
    ▸ subdir1/
    ▸ subdir2/
    .DS_Store
    categories.json
    guides.json
    index.html
    jsduck.json
    videos.json

できたらアクセス。

release配下のindex.htmlにブラウザからアクセスしてみます。
こんな感じになりました。




Gitにのっけました。

release配下のindex.htmlで上の出来上がりが確認できるかと思います。

https://github.com/froggugugugu/jsducktest

ビデオを見てみる

紹介されているように、ビデオを見てみます。
http://vimeo.com/33465319
4分17秒あたりから上記のカスタムパラメタとリンクする各メニュータブの説明
6分27秒あたりからソース内のマークダウン記法について あーそうなんだ。コメントもマークダウンでかけばいいのね。で{@link}とかなわけだ。

インストールについて

16分56秒あたりからインストールです。

タイトルについて

18分42秒あたりからタイトルの変更。
19分あたりからコメントブロック入れようね。
20分あたりからソース内のパラメータに沿ったDocumentの吐き出され方 alternateClassNameとかsingletonとか。 設定するとアイコンが変わったり、とか、こう出るよ。的な。
21分でコメントブロックの記載について。 Ext.define直前のドキュメントコメントはクラス説明文にあたるみたい。

propertyについて

21分50秒付近でのプロパティ直前のドキュメントコメントについては
/**
 * @property {[型]} [名前] [説明]
 */

configオプションについて

22分40秒付近ではコンフィグオプションの説明
config:{
    /**
     * @cfg
     * [説明]
     */
    hogehoge: 0.5
}
だと、0.5を判断して型を表示して、ConfigOptionカテゴリに分類して出力してくれるみたい。

functionについて

23分37秒付近ではfunctionの説明 functionの直前のコメントブロックに
/**
 * [説明]
 * @return {[型]} 説明
 */
とすることでMethodカテゴリに分類して出力してくれる。

functionについて(戻り値Object)

25分07秒付近ではfunctionの説明(Object戻り)
/**
 * [説明]
 * @return {Object}
 * @return {[型]} return.[Objectのメンバ名] 説明
 * @return {[型]} return.[Objectのメンバ名] 説明
 * ...
 */
とすることで戻りがObjectの際の出力をしてくれる。

functionについて(alias)

26分30秒付近ではfunctionの説明(alias)
/**
 * @alias クラス名#メソッド名
 */
で、内容が同一になるし、
/**
 * @alias [クラス名#メソッド名]
 * @duprecated [説明] {@link}
 */
で、重複の警告が出るみたい。 {@link [クラス名#メソッド名]}で本文内リンク。

eventについて

29分20秒付近ではeventの説明
/**
 * @event [イベント名]
 * [説明]
 * @param {[型]} [説明]
 */

/**
 * @event [イベント名] [説明]
 */
でイベントの説明を

privateクラスについて

31分00秒付近ではprivateクラスの説明 Ext.define直前のドキュメントコメントで
/**
 * @private
 */
とするとprivate クラスになります。

exampleコードの機能について

31分36秒付近では例の説明 Ext.define直前のドキュメントコメントで
/**
 * @example
 *     [code....]
 */
と,するわけですが この@exampleがなくても4space入れてるのでmarkdown形式で
コードブロックとなるわけですが、プレビュー機能等をつけるときは
この@exampleをつけます。
この際、eg-iframe.htmlを利用するので
配下にあるeg-iframe.htmlに当該jsファイルを読み込む設定を
しておきます。
ビルドするときはパラメタに–eg-iframe=[eg-iframe.htmlのパス]を設定します。

imageについて

34分00秒付近からイメージの説明 Ext.define直前のドキュメントコメントで {@img [ファイル名]} としておきます。 この際、ビルドするときはパラメタに–images=[画像が入ったフォルダパス]を設定します。

categoriesの設定について

36分00秒付近からイメージの説明 作業用ディレクトリ直下にcategories.jsonファイルを作って、、、下記のような内容で記載。
[
    {
        "name": "Jsducktest.js",
        "groups": [
            {
                "name": "subdir1",
                "classes": [
                    "class1_1",
                    "class1_2"
                ]
            },
            {
                "name": "subdir2",
                "classes": [
                    "class2_1",
                    "class2_2"
                ]
            }
        ]
    }
]
groupsで括って、 classesで括ってクラスの羅列。 この際、ビルドするときはパラメタに–categories=categories.jsonを設定します。

guidesの設定について

39分20秒付近からguidesの説明 作業用ディレクトリ直下にguides.jsonファイルを作って、、、下記のような内容で記載。
[
    {
        "title": "RootNode",
        "items": [
            {
                "name": "subdir1",
                "title": "サブディレクトリ1",
                "description": "解説1"
            },
            {
                "name": "subdir2",
                "title": "サブディレクトリ2",
                "description": "解説2"
            }
        ]
    }
]
itemsで括って、 nameにはguides配下のサブディレクトリ名と合わせる。 で、imageを変えたい場合はREADME.mdの同じ階層にicon-lg.pngを配置。 このあたり見ると良いかも。
https://github.com/senchalabs/jsduck/blob/master/lib/jsduck/guides.rb
この際、ビルドするときはパラメタに–guides=guides.jsonを設定します。

welcomeの設定について

42分22秒付近からwelcomeページの設定について 作業用ディレクトリ直下にwelcome.htmlファイルを作って、、、
この際、ビルドするときはパラメタに--welcome=welcome.htmlを設定します。

videosの設定について

43分18秒付近からvideosの設定について 作業用ディレクトリ直下にvideos.jsonファイルを作って、、、下記のような内容で記載。
[
    {
        "title": "RootNode",
        "items": [
            {
                "id": "9071202",
                "title": "サブディレクトリ1",
                "description": "解説1",
                "thumb": "http://b.vimeocdn.com/ts/439/819/439819_200.jpg"
            }
        ]
    }
]
vimeoのIDを入れるようです。。 この際、ビルドするときはパラメタに–videos=videos.jsonを設定します。

examlesの設定について

44分40秒付近からexamplesの設定について 作業用ディレクトリ直下にexamples.jsonファイルを作って、、、下記のような内容で記載。
[
    {
        "title": "RootNode",
        "items": [
            {
                "text": "例",
                "url": "[index.html]",
                "icon": "[icon]",
                "desc": "解説1"
            }
        ]
    }
]
この際、ビルドするときはパラメタに–examples=examples.jsonと設定します。

なんだよ、、まぁ、ここまで見てアレですが、

47分20秒付近から
https://github.com/senchalabs/jsduck/wiki/Guide
https://github.com/senchalabs/jsduck/wiki/Advanced-Usage
に今までの解説がありました。。。。

パラメタの設定について

コマンドラインパラメタは --configで指定できます。前述でも挙げましたが、こいつは便利ですね。

2012年7月7日土曜日

作業部屋の改修が一段落できた。


作業部屋、スペースの都合上
一度は座卓にして椅子を廃止したんですが、
座卓にすると長居ができず、
椅子の頃よりも長時間の作業がしづらいので
また椅子に戻すことにしました。
ということに着手してから数ヶ月。。
(このネタ書き始めてから半年経つとか、、ありえんわ。。)
数時間で出来る作業を
超細切れの時間でちょこちょこちょこちょこと
作業してようやく形になった。

何をしたか

座卓をやめました。

座卓の頃
こいつを分解します。


座卓の分解

もともと部屋幅ぎりぎりのサイズで買ったPCデスクだったので
組み立てるのはかなりハードでした。
ドライバーが入りませんから。。
で、組み立てるのが大変なら、分解するのも大変です。
ドライバが入らないので。。
とドライバー用ビットを組み合わせて
隙間用ラチェットドライバとして作業をしました。
これがかなり隙間仕事に便利

こんな具合です。

この数センチの隙間作業。。
隙間仕事、大変です。
イライラ度は満点です。
分解して運びました。


再構築

そもそもデスクタイプにしようとした理由の一つとして
足の入場とかスペースが厳しい。などありましたので
PCデスクを分解して天板のみを再利用する。
という方針にしました。


天板の固定

椅子の高さ=身長の1/4
椅子から机までの感覚=身長の1/6
とのことなのでそのようにします。


問題は机の天板が固定できないということ。。
壁板は薄いので、、、
壁板にそのまま天板固定用の土台となる
柱を打ち付けられません。。
ので、部屋加工用(本棚設置用)につけた柱
ベースに作業を行います。
左右にうまい具合に支え板を取り付けることにしました。

本棚設置用の柱(こいつのお陰で好き勝手し放題Adapterだなww)
柱に支えを打ち付けてその上に天板を乗せるサンを載せこむ。と。

材料、材料
バシバシとノコ引きます。

途中、ほぞ?作るのにノミがない無いことに気が付き、
電動ドリルで穴開けまくって、ぶん殴ってほぞを作りました。
これは焦った。

点はつなげると線になりますね。よかった。よかった。

まぁそれらしくなりました。

あとは上に乗せるだけ


ぐちゃぐちゃだけど入れ物は整った。これから整理整頓です。

のせました。
できました!!
子供の相手しながら
隙間をみて買い物して
隙間をみてのこぎり引いて
隙間をみて掃除機かけて
隙間をみて組み立てて
中断するたびにすべて部屋の中に片付けて
再会するときにすべてをへやから出して、、
のループをひたすら繰り返しました。
実作業よりも片づけと用意に割いた時間が
長かった気がする。
まとまった時間がほしいなぁ
と思った作業でした。。
おわり。