さて、前回に引き続き、素振り2スイング目。
1スイング 目では導入部分的なことをやった。
一瞬ハマったこと
前回、scaffold generatorでmembersを作った。
だからlocalhost:8080/membersにアクセス出来るはずなんだけど、Inernal Sever Errorが。
org.h2.jdbc.JdbcSQLException: 機能�サ�ート�れ����ん: "autoServerMode && (readOnly || fileLockMethod == NO || fileLockMethod == SERIALIZED || fileLockMethod == FS || inMemory)" Feature not supported: "autoServerMode && (readOnly || fileLockMethod == NO || fileLockMethod == SERIALIZED || fileLockMethod == FS || inMemory)" [50100-191] at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) at org.h2.message.DbException.get(DbException.java:179) at org.h2.message.DbException.get(DbException.java:155) at org.h2.message.DbException.getUnsupportedException(DbException.java:216) at org.h2.engine.Database.open(Database.java:639) at org.h2.engine.Database.openDatabase(Database.java:270) at org.h2.engine.Database.(Database.java:264) at org.h2.engine.Engine.openSession(Engine.java:65) at org.h2.engine.Engine.openSession(Engine.java:175) at org.h2.engine.Engine.createSessionAndValidate(Engine.java:153) at org.h2.engine.Engine.createSession(Engine.java:136) at org.h2.engine.Engine.createSession(Engine.java:28) at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:349) at org.h2.jdbc.JdbcConnection. (JdbcConnection.java:107) at org.h2.jdbc.JdbcConnection. (JdbcConnection.java:91) at org.h2.Driver.connect(Driver.java:72) at java.sql.DriverManager.getConnection(DriverManager.java:664) at java.sql.DriverManager.getConnection(DriverManager.java:247) at org.apache.commons.dbcp2.DriverManagerConnectionFactory.createConnection(DriverManagerConnectionFactory.java:77) at org.apache.commons.dbcp2.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:256) at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363) at org.apache.commons.dbcp2.PoolingDataSource.getConnection(PoolingDataSource.java:134) at scalikejdbc.Commons2ConnectionPool.borrow(Commons2ConnectionPool.scala:46) at scalikejdbc.DB$.readOnly(DB.scala:172) at scalikejdbc.SQLToResult$class.apply(SQL.scala:578) at scalikejdbc.SQLToOptionImpl.apply(SQL.scala:736) at skinny.orm.feature.CalculationFeature$class.count(CalculationFeature.scala:30) at model.Member$.count(Member.scala:17) at skinny.orm.feature.CRUDFeatureWithId$class.countAllModels(CRUDFeature.scala:281) at model.Member$.countAllModels(Member.scala:17) at skinny.controller.SkinnyApiResourceActions$class.countResources(SkinnyApiResourceActions.scala:150) at controller.MembersController.countResources(MembersController.scala:8) at skinny.controller.SkinnyResourceActions$$anonfun$showResources$1.apply(SkinnyResourceActions.scala:84) at skinny.controller.SkinnyResourceActions$$anonfun$showResources$1.apply(SkinnyResourceActions.scala:80) at skinny.controller.feature.SkinnyControllerCommonBase$class.withFormat(SkinnyControllerCommonBase.scala:98) at skinny.controller.SkinnyController.withFormat(SkinnyController.scala:8) at skinny.controller.SkinnyResourceActions$class.showResources(SkinnyResourceActions.scala:80) at controller.MembersController.showResources(MembersController.scala:8) at skinny.controller.SkinnyResourceRoutes$$anonfun$1.apply(SkinnyResourceRoutes.scala:16) at skinny.micro.SkinnyMicroBase$class.liftAction(SkinnyMicroBase.scala:302) at skinny.controller.SkinnyController.liftAction(SkinnyController.scala:8) at skinny.micro.SkinnyMicroBase$$anonfun$invoke$1.apply(SkinnyMicroBase.scala:296) at skinny.micro.SkinnyMicroBase$$anonfun$invoke$1.apply(SkinnyMicroBase.scala:296) at skinny.micro.ApiFormats$class.withRouteMultiParams(ApiFormats.scala:196) at skinny.controller.SkinnyController.withRouteMultiParams(SkinnyController.scala:8) at skinny.micro.SkinnyMicroBase$class.invoke(SkinnyMicroBase.scala:295) at skinny.controller.SkinnyController.invoke(SkinnyController.scala:8) at skinny.micro.SkinnyMicroBase$$anonfun$runActions$1$2.apply(SkinnyMicroBase.scala:208) at skinny.micro.SkinnyMicroBase$$anonfun$runActions$1$2.apply(SkinnyMicroBase.scala:208) at scala.collection.immutable.Stream.flatMap(Stream.scala:489) at skinny.micro.SkinnyMicroBase$class.runActions$1(SkinnyMicroBase.scala:208) at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply$mcV$sp(SkinnyMicroBase.scala:241) at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply(SkinnyMicroBase.scala:241) at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply(SkinnyMicroBase.scala:241) at skinny.micro.SkinnyMicroBase$class.cradleHalt$1(SkinnyMicroBase.scala:215) at skinny.micro.SkinnyMicroBase$class.executeRoutes(SkinnyMicroBase.scala:239) at skinny.controller.SkinnyController.executeRoutes(SkinnyController.scala:8) at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply$mcV$sp(SkinnyMicroBase.scala:110) at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply(SkinnyMicroBase.scala:110) at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply(SkinnyMicroBase.scala:110) at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) at skinny.micro.base.SkinnyContextInitializer$class.withResponse(SkinnyContextInitializer.scala:106) at skinny.controller.SkinnyController.withResponse(SkinnyController.scala:8) at skinny.micro.base.SkinnyContextInitializer$$anonfun$withRequestResponse$1.apply(SkinnyContextInitializer.scala:81) at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) at skinny.micro.base.SkinnyContextInitializer$class.withRequest(SkinnyContextInitializer.scala:94) at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8) at skinny.micro.base.SkinnyContextInitializer$class.withRequestResponse(SkinnyContextInitializer.scala:80) at skinny.controller.SkinnyController.withRequestResponse(SkinnyController.scala:8) at skinny.micro.SkinnyMicroBase$class.handle(SkinnyMicroBase.scala:109) at skinny.controller.SkinnyController.skinny$micro$contrib$FlashMapSupport$$super$handle(SkinnyController.scala:8) at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply$mcV$sp(FlashMapSupport.scala:65) at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply(FlashMapSupport.scala:38) at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply(FlashMapSupport.scala:38) at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) at skinny.micro.base.SkinnyContextInitializer$class.withRequest(SkinnyContextInitializer.scala:94) at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8) at skinny.micro.contrib.FlashMapSupport$class.handle(FlashMapSupport.scala:38) at skinny.controller.SkinnyController.skinny$micro$contrib$ScalateSupport$$super$handle(SkinnyController.scala:8) at skinny.micro.contrib.ScalateSupport$class.handle(ScalateSupport.scala:124) at skinny.controller.SkinnyController.handle(SkinnyController.scala:8) at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply$mcV$sp(SkinnyMicroFilterBase.scala:29) at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply(SkinnyMicroFilterBase.scala:29) at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply(SkinnyMicroFilterBase.scala:29) at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) at skinny.micro.SkinnyMicroFilterBase$class.doFilter(SkinnyMicroFilterBase.scala:28) at skinny.controller.SkinnyController.doFilter(SkinnyController.scala:8) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) at org.eclipse.jetty.server.Server.handle(Server.java:499) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257) at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) at java.lang.Thread.run(Thread.java:745)
なんだなんだ、JDBCの設定の問題かなぁ。。。とか思ったんですが、ファイルパーミッションの問題でした。てへぺろ。
無心でコマンド打ったら無事解決。
chmod -R 755 <プロジェクト名>
skinnyコマンドをもうちょっと調べる
これはもしかして、コマンドさえ駆使出来れば、かなり定型作業が省略されるのでは。
ということでコマンドを調べてみる。
skinny Usage: ./skinny [COMMAND] [OPTIONS]... new : will create new Skinny application run/server/s : will run application for local development run -precompile : will run application with Scalate precompilation debug/d : will run application with JDWP. default port 5005 clean : will clear target directory update : will run sbt update console : will run sbt console compile : will compile all the classes ~compile : will compile all the classes when changes are detected db:migrate : will execute database migration db:repair : will recover when previous migration failed routes : will display routes information test : will run all the tests ~test : will run all the tests when changes are detected testQuick : will run only failed tests ~testQuick : will run only failed tests when changes are detected testOnly : will run the specified test ~testOnly : will run the specified test when changes are detected test:coverage : will run all the tests and output coverage reports package : will create *.war file to deploy package:standalone : will create *.jar file to run as stand alone app publish : will publish *.war file to repository scalajs:watch : will watch Scala.js Scala code change and convert to JS scalajs:package : will convert Scala.js Scala code to JS file eclipse : will setup Scala IDE settings idea/gen-idea : will setup IntelliJ IDEA settings task:clean : will clean task project's target directory task:run : will run tasks g/generate controller : will generate controller g/generate model : will generate model g/generate migration : will generate db migration file g/generate scaffold : will generate scaffold files with ssp templates g/generate scaffold:scaml : will generate scaffold files with scaml templates g/generate scaffold:jade : will generate scaffold files with jade templates g/generate reverse-scaffold : will generate scaffold from existing database g/generate reverse-scaffold:scaml : will generate scaffold from existing database g/generate reverse-scaffold:jade : will generate scaffold from existing database
ふむ。特別変わったコマンドは無いですね。
あえて注目するとすればgenerate系。
モデル、コントローラー、マイグレーションファイルの生成、
素振りの1回目 でやったscaffold。
DBからreverseでscaffoldも出来る。このあたりを抑えておけば問題はなさそう。
自動生成されたページを見てみる
さて、改めてlocalhost:8080/membersにアクセス。
これが
こうなって
こうなって
こうじゃ
もはや何もせずともCRUD処理が出来てしまう。
ここまでScalaのコーディングはおろか、設定ファイルすらいじってない。
すごいなぁ。
生成されたソースを一度じっくり見る
ここで何が大事かを考える。と
・何が作られているか
・どういう仕組みで作られているか
の大枠をつかむことかなぁと思う。
ということでソースを読む。
とりあえずフォルダ構成的なもので言うと
src/main/scala
src/main/resources
これはまぁ、Scalaの基本的なアレだろう。
treeコマンド打ったら良いよね。
雑にこんな感じ
tree -f -I "bin|target|test|build|db|logs|project|task" -P "*.[class]" . ├── ./src │ └── ./src/main │ ├── ./src/main/resources │ ├── ./src/main/scala │ │ ├── ./src/main/scala/controller │ │ ├── ./src/main/scala/lib │ │ ├── ./src/main/scala/model │ │ ├── ./src/main/scala/templates │ │ └── ./src/main/scala/worker │ └── ./src/main/webapp │ ├── ./src/main/webapp/WEB-INF │ │ ├── ./src/main/webapp/WEB-INF/assets │ │ │ ├── ./src/main/webapp/WEB-INF/assets/coffee │ │ │ ├── ./src/main/webapp/WEB-INF/assets/jsx │ │ │ ├── ./src/main/webapp/WEB-INF/assets/less │ │ │ ├── ./src/main/webapp/WEB-INF/assets/scala │ │ │ └── ./src/main/webapp/WEB-INF/assets/scss │ │ ├── ./src/main/webapp/WEB-INF/layouts │ │ └── ./src/main/webapp/WEB-INF/views │ │ ├── ./src/main/webapp/WEB-INF/views/error │ │ ├── ./src/main/webapp/WEB-INF/views/members │ │ └── ./src/main/webapp/WEB-INF/views/root │ └── ./src/main/webapp/assets │ ├── ./src/main/webapp/assets/css │ └── ./src/main/webapp/assets/js └── ./standalone-build
ということで、controller、model、templates、workerなどが出来てる。 WEB-INF/assetsは、テンプレートエンジンのアレをアレしてるものだと思われる。
WEB-INF/views/membersが僕が作ったscaffold作ったもの。
Model
モデルを見てみると、本当にシンプルなcase classで定義されたドメインクラスとSkinnyCRUDMapperというtrait実装のみ。
SkinnyCRUDMapperではResultSetをwrapしたものをメンバーにバインドするextractメソッドがあるのみ。
View
viewとしてはこんなものが出来ている。
_form.html.ssp・・・・共通的なフォーム
edit.html.ssp・・・編集(CRUDでいうUPDATE)
index.html.ssp・・・雑に言うとindexページ。ドメインの一覧表示をする画面になっている。
new.html.ssp・・・新規(CRUDでいうCREATE)
show.html.ssp・・・詳細表示(CRUDでいうREAD)
i18n対応もされている。っぽく見える。
Controller
ControllersというクラスでServletContextにマウントしている。
主にルーティングはここでやっているように見える。
MembersControllerというクラス(scaffoldしたやつ)でバリデーション、各CRUD操作の実装(呼び出し)がされている。
抽象的な処理はSkinnyResourceでほぼ完了している感じ。
SkinnyResourceはロギング、Skinny Micro、TypeConverter、SkinnyMapperなどを持った
共通的なコンテキストオブジェクトってイメージが正しいのかなぁ。
次回予告
長くなったのでブログ記事ここまで!!
本当に良く出来ているので、何もしていないのにこんなに長くなってしまう。。
次回はもう少しSkinnyの内部の実装を追っていきたいところ。
あとは、ドキュメント読みつつサンプル真似してコード書くとか。
0 件のコメント:
コメントを投稿