今後やること
前回の続き。
今回も書き散らしで申し訳ないですが、
自分のエンジニアとしての市場価値を上げる方法を考えました。
できなければならないこと
IT周りの能力を磨く
プログラミング能力
- スピード
- アウトプットが速い
- 質
- 可読性が高い
- パフォーマンスを考慮している
- 変更しやすい
- スピード
一部の分野に関する専門的な(あまり人が知らないような)知識、経験
- まだ自分の中では決まっていない
流行の技術に関する全般的な知識
- どういう技術なのか
- 何故流行っているのか、その経緯
この部分は同年代のエンジニアを見ると明らかに足りていないと感じる。
Soft Skillsを鍛える
- 周りを巻き込みながらプロダクトを作る能力
- 意識しながら業務を回すことで培うことができるはず
自分の市場を広げる
- IT技術が必要なのは世界共通
- 日本市場だけでなく、世界市場で買ってもらえるようにする
実際にやること
毎日プログラミングする
output
良いコードを速く書く。
- 読みやすいコード
- 変更しやすいコード
- パフォーマンスを考慮したコード
- 言語の機能を最大限利用したコード
技術力を試される場には極力参加する。
コンテストやコンペだけでなく、Tech talkなどの場にも積極的に。
「どの技術を使うべきか」とかごちゃごちゃ考えていたけど、 そこで悩み続けているのは時間の無駄だし、結局どの選択が一番良いかなんてあとになってみないとわからない。 何らかの技術選択をしたとしても、個人で使う分には事業に悪影響を及ぼすわけでもなく、リスクが無いので とりあえず使ってみてこれは駄目だと思ったら変えて、というスタイルが一番いいと思った。
input
- 出られる勉強会には極力参加する。
- 自分の使っている言語やFrameworkのつくりを理解したり、コードを読んだりする。
- 自分の使っている技術に今後必要な機能を考えて、
ライブラリを作るプロジェクトに参加したり、自分で興したりする。
Soft Skillsを鍛える
- 具体的に何をすべきか、というところがまだ自分の中にない。
- 本を読む。
英語で読み書きできるようにする
- レアジョブ
- OSS contribute
- その他。まだ考えられていない
こんなところでまた明日考える。
今の自分の能力と今後のキャリアについて
RubyKaigi2017に参加させていただきました。
いろんな発表を聞いて、自分のエンジニアとしての価値について考えることがあったので、今後のキャリアも含めて考えたことをつらつらと書いていきます。
現状の自分のエンジニアとしての価値
1. プログラミング能力
Ruby on Railsがそこそこ書ける
- MVCにそれぞれの役割を理解した上でコードが書ける。
- システム要件に即したビジネスロジックが書ける。
- エラーハンドリングができる。
- アプリケーションコードレベルでパフォーマンスを考えたクエリが書ける。
- 可読性の高いコードが書ける。
(社外でコードレビューを受けたことがないので、人によってはクソコードと思うかもしれない)
概念の切り分け方を考えられる
- 機能によって、プログラミング言語に合わせた概念の切り分けが行える。
- Railsで言うとClass(内部Classも含める), Module, Concernなど。
- クラス命名をアンチパターンを踏まえながら適切に命名できる。
- ディレクトリ構成も概念によって切り分けを意識的に行う。
- とはいえベストプラクティスが浮かばないような場面も多々ある。
ソフトウェア開発を取り巻く概念をある程度理解している
2. Soft Skills
Soft Skillsを参照のこと。
粗筋から参照すると、
対人的な交渉・指導・意思疎通などをうまく行える能力や知恵
要件定義ができる
- 依頼者からのビジネス要件をヒアリングできる。
- 依頼者が本当にやりたいことは何かを考え、提案できる。
- 全くの新機能なのか、既存機能に対する追加機能なのかを踏まえて
工数見積を行える。 - 依頼者に「なぜこれだけ時間がかかるのか」を伝え、交渉できる。
エンジニアとして世の中ででかい価値を出す方法
1. 何らかの技術のエキスパートになる
- プログラミング言語やフレームワークをよく知っている、もしくは作っている人になって、
その言語やフレームワークを利用している会社のアドバイザになる。 - 提供するサービスの性質上、利用するプログラミング言語が限定されており、
かつその言語を書ける人が少ない場合は、それだけでプログラマとして食っていけると思う。
例えばミドルウェアの会社や組み込みのソフトウェアの会社でプログラムを書くとか、
恐ろしく速さが求められるサービスをフルスクラッチで作る、とかいった状況の場合
使われる言語はC++とかRustとか?だと思うが、これらを書ける人はインタプリタ言語よか少なそうなので、
書けるだけで価値があると思う。
つまり学ぶのが難しくて、かつそのツールを使う理由があるような場では
それが使える人は価値が出せるということ。
2. エンジニア目線で事業に貢献できる人になる
- 流行りの枠組み、例えばWebサービスならWebサービスを構成する主要な要素の基本的な仕組みと、
代表的なアプリケーションの特徴を知っており、技術選択ができるレベルならば、
アーキテクトとして食っていけそう。 - エンジニアとビジネスサイドの間に立つ人として、予算レベルの折衝からプロダクトの要件定義、 工数見積までできれば、事業を回せる人として価値が出そう。
3. スタートアップを起こす
- 思いついたサービスを実現してユーザに使ってもらう。
- 技術の限界に挑戦するようなスタートアップでない限り、ものを作るエンジニアというよりかは
速く書いて、ユーザと話して、VCと資金の交渉をして、といった、Soft Skillsが強く求められそう。
今日はここまで。
今後も自分の歩めるキャリアの選択肢と、自分がどういうキャリアを歩みたいか、ということを整理して意識化していきたい。
Ruby on Rails 画面表示情報に関する悩み
Railsを書いていて、画面に渡す情報が変わったときに、やりたいことはシンプルなのに内部ロジックの変更は多くて、しかも自分のやり方が正しいのかどうかもわからないのでいつもすごくもやもやする。
抽象化できてなくてわかりづらいと思いますが、備忘録としてつらつらと書いていきます。
前提
下記のようなテーブルがあるとする。
users
column_name | desc |
---|---|
id | なし |
name | ユーザ名 |
password | パスワード |
user_name_timestamps
column_name | desc |
---|---|
id | なし |
user_id | users テーブルのID |
name | ユーザ名 |
created_at | レコード生成日時 |
updated_at | レコード更新日時 |
users
とuser_name_timestamps
は1:N
で結びついていて、
ユーザがユーザ名を変更するたびに、変更前のユーザ名をuser_name_timestamps
レコードとして保存しておく。
/app/models/user.rb
class User < ApplicationRecord has_many :user_name_timestamps end
/app/models/user_name_timestamp.rb
class UserNameTimestamp < ApplicationRecord belongs_to :user def stamp!(user) self.create!(user_id: user.id, name: user.name) end end
UsersController
は下記のようになっている。
/app/controllers/users_controller.rb
class UsersController < ApplicationController ... def show @user = User.find_by(params[:id]) end end
User#show
画面に「ユーザ名変更履歴」というリンクがあって、リンクをクリックするとUsers::NameTimestamps#index
に飛ぶ。
app/controllers/users/name_timestamps_controller.rb
class Users::NameTimestampsController < ApplicationController def index @user = User.includes(:user_name_timestamps).find_by(params[:user_id]) @name_timestamps = @user.user_name_timestamps end end
viewでは@user.id
や@user.name
を表示してどのユーザかをわかるようにし、
@name_timestamps.each do |t|
でタイムスタンプをリスト表示するようにしている。
悩みごと
下記のようなビジネス要件が来たとする。
『ユーザ詳細画面で、タイムスタンプのリンクをクリックするのがめんどくさい』というユーザからの意見が多い。 リンクをクリックしなくても、ユーザ詳細画面で見れるようにしてほしい。
現状の実装に改修を加えるとすると、素朴に考えたらこうだろうか。
/app/controllers/users_controller.rb
class UsersController < ApplicationController ... def show @user = User.includes(:user_name_timestamps).find_by(id: params[:id]) @name_timestamps = user.user_name_timestamps end end
resourceが2つあるのが気持ち悪いので、UserWithNameTimestamp
クラスを定義してまとめる。
/app/usecases/user_with_name_timestamp.rb
class UserWithNameTimestamp attr_reader :user, :name_timestamps def initialize(user_id) @user = User.includes(:user_name_timestamps).find_by(id: user_id) @name_timestamps = user.try!(:user_name_timestamps) end end
これでUsers#show
のresourceが1つにまとまった。
/app/controllers/users_controller.rb
class UsersController < ApplicationController ... def show @user_with_name_timestamps = UserWithNameTimestamp.new(params[:id]) end end
UsersController
が意識するresourceは1つになったけど、これ、扱っているresourceがUsers
じゃなくてUserWithNameTimestamp
だからUsersController
じゃないな。命名を変更するか。
と思ったけど、よく見たら他のactionではUser
がresourceとして使われている。
class UsersController < ApplicationController ... def create User.create!(params) redirect_to users_index_path end def show @user_with_name_timestamps = UserWithNameTimestamp.new(params[:id]) end end
…じゃあ別のControllerを作るしかない。
Users::WithNameTimeStampsController
を定義する。
/app/controllers/users/with_name_timestamps_controller.rb
class Users::WithNameTimestampsController < ApplicationController def show @user_with_name_timestamps = UserWithNameTimestamp.new(params[:id]) end end
これでUsers#show
は機能としては要らなくなるけど、user_id
パラメータはなんとかして渡さなきゃいけないし、
Users#show
actionに当たるものは存在しますよ、と明示しておきたいので、下記のようにする。
/app/controllers/users_controller.rb
class UsersController < ApplicationController ... def show redirect_to users_with_name_timestamps_show_path, {id: params[:id]} end end
Users::NameTimestampsController
は不要になる。画面としては消しておきたいので、actionをコメントアウトしておいて、後々ファイルごと削除する。
/app/controllers/users/name_timestamps_controller.rb
class Users::NameTimestampsController < ApplicationController # def index # @user = User.includes(:user_name_timestamps).find_by(params[:user_id]) # @name_timestamps = @user.user_name_timestamps # end end
…やることは終わったけど、これってやり方としてどうなんだろう。
「resrouce1つに対してcontrollerは1つ」を守るならば、改修後のUsers#show
はそれを守っていると言えるのか?と言われたら微妙だと思う。UsersControllerはなくして、User
に対するCRUDは全部UserWithNameTimestamp
クラスのメソッドとして定義し直せば、Users::WithNameTimestampsController
に1つのリソースとしてまとめられるけど、そんなことわざわざする…? とも思うし。
言いたいことの説明が下手くそすぎるんですけど、もしご意見、感想などありましたら是非いただきたいです。
Play Scala ViewからDBレコード追加
モデル追加に続いて、ViewからDBレコードのCLUDができるようにする。
前回に引き続き、こちらを参考に。 qiita.com
routesの設定
/conf/routes
1 # Routes 2 # This file defines all application routes (Higher priority routes first) 3 4 GET / controllers.HomeController.index 5 GET /article controllers.ArticleController.index # added 6 POST /article controllers.ArticleController.create # added 7 8 # Map static resources from the /public folder to the /assets URL path 9 GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
今回はArticlesController
ではなくArticleController
と命名した。
Ruby on RailsではController名は複数形で書くことを推奨されている。
「推奨されている」というのはどういうことかというと、Controllerが複数形で書かれていれば、resources
で{resource}s_{action}_path
のように、Railsがパスを自動生成してくれる。
が、この「Controller名は複数形で書くべき」という設計(思想?)は間違っているのでは?と思う。
Railsの開発者であるDHHは、Controllerのアクションは全部resourcesで間に合わせるべきで、それ以外のアクションが必要になったらController自体を分けることを推奨している。
でも、全部resources
で書くべきならば、ControllerによってはORM(Railsの場合はActiveRecord)を継承しないクラスを扱うこともあるだろうし、Controller内でindex
アクションを使わないならば、それ以外のresourcesのアクションに関してはresourceが複数であることを意識しないので、どちらかというと単数形で統一するのが正しいのではないかと思う。
Play Scalaでは、今のところ「Controllerは複数形を使ってね」と言われていないので、
単数形で書くことにした。
evolutionsの修正
テーブル名が複数形になっていないのが気持ち悪いので、全部複数形に直す。
/conf/evolutions/default/1.sql
1 # --- !Ups 2 3 CREATE TABLE "users" ( # user -> users 4 "username" TEXT NOT NULL, 5 "password" TEXT NOT NULL, 6 "name" TEXT NOT NULL 7 ); 8 9 CREATE TABLE "articles" ( # article -> articles 10 "user_id" INT NOT NULL, 11 "content" TEXT NOT NULL 12 ); 13 14 CREATE TABLE "comments" ( # comment -> comments 15 "user_id" INT NOT NULL, 16 "article_id" INT NOT NULL, 17 "content" TEXT NOT NULL 18 ); 19 20 # --- !Downs 21 22 DROP TABLE "users"; # user -> users 23 DROP TABLE "articles"; # article -> articles 24 DROP TABLE "comments"; # comment -> comments
DAOの修正
dao
ディレクトリが複数形になっていなかったので直す。- evolutionsの修正にて、複数形に直したテーブルを参照するように修正する。
/app/daos/articleDAO.scala
1 package daos // dao -> daos 2 3 import scala.concurrent.Future 4 5 import javax.inject.Inject 6 import models.Article 7 import play.api.db.slick.DatabaseConfigProvider 8 import play.api.db.slick.HasDatabaseConfigProvider 9 import play.api.libs.concurrent.Execution.Implicits.defaultContext 10 import slick.driver.JdbcProfile 11 12 class ArticleDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] { 13 import driver.api._ 14 15 private val Articles = TableQuery[ArticlesTable] 16 17 def all(): Future[Seq[Article]] = db.run(Articles.result) 18 19 def insert(article: Article): Future[Unit] = db.run(Articles += article).map { _ => () } 20 21 private class ArticlesTable(tag: Tag) extends Table[Article](tag, "articles") { // article -> articles 22 23 def user_id = column[Int]("user_id") 24 def content = column[String]("content") 25 26 def * = (user_id, content) <> (Article.tupled, Article.unapply _) 27 } 28 }
uerDAO
, commentDAO
も同様に修正(省略)。
Controllerの設定
例に倣って書く。
/app/ArticleController.scala
1 package controllers 2 3 import javax.inject._ 4 import play.api._ 5 import play.api.mvc._ 6 import play.api.db._ 7 8 import daos.ArticleDAO 9 import models.Article 10 import play.api.data.Form 11 import play.api.data.Forms.mapping 12 import play.api.data.Forms.number 13 import play.api.data.Forms.text 14 import play.api.libs.concurrent.Execution.Implicits.defaultContext 15 16 @Singleton 17 class ArticleController @Inject() (articleDao: ArticleDAO) extends Controller { 18 def index = Action.async { 19 articleDao.all().map { 20 articles => Ok(views.html.article.index(articles)) 21 } 22 } 23 24 def create = Action.async { implicit request => 25 val article: Article = articleForm.bindFromRequest.get 26 articleDao.insert(article).map(_ => Redirect(routes.ArticleController.index)) 27 } 28 29 val articleForm = Form( 30 mapping( 31 "user_id" -> number, 32 "content" -> text() 33 )(Article.apply)(Article.unapply) 34 ) 35 }
ちなみに31行目、user_id
はDB上ではINT
、Modelとしてもint
として扱っているので、formでtext()
にmappingするとエラーになる。
Playのドキュメントを漁ってみると、int型を扱うにはnumber
が適切っぽいのでnumber
を設定した。
Viewの設定
これも例に倣う。
/app/views/article/index.html.scala
1 @(articles: Seq[Article]) 2 @main("Article database") { 3 <div> 4 <div id="articles"> 5 <h2>Insert an article here:</h2> 6 7 <form action="/article" method="POST"> 8 <input name="user_id" type="text" placeholder="user_id"/> 9 <input name="content" type="text" placeholder="content"/> 10 <input type="submit"/> 11 </form> 12 13 <h2>Previously created articles:</h2> 14 <table> 15 <tr><th>UserId</th><th>Content</th></tr> 16 @for(a <- articles){ 17 <tr><td>@a.user_id</td><td>@a.content</td></tr> 18 } 19 </table> 20 </div> 21 </div> 22 }
実行してみる
sbt run
してhttp://localhost:9000/article
にアクセスしたら問題なく動作しました。
レコードのcreate
もちゃんとできた。
@playdb, PostgreSQL
playdb=> select * from articles; user_id | content ---------+------------------ 1 | hogehoge 2 | 日本語 3 | Play Scala 4 | ぜったいってなに (4 rows)
次はModelにビジネスロジックの実装かな。
Controllerに関しては他のresourcesアクションを追加するくらいしかやることないだろうし、
Viewに関してはControllerから値が渡せればサーバーサイドエンジニアとしてはやること終わりだと思うし。
Play Scala モデル追加
DB connectionができたので、次はモデルを記述して、
モデルからテーブルのレコードをCRUDできるようにする。
こちらの記事をまるっと参考にさせていただきました。
まずは昨日時点のevolutionファイルを修正する。
PostgreSQLでは大文字小文字が区別されないようなので、
テーブル名、カラム名を小文字で統一。
/conf/evolutions/default/1.sql
1 # --- !Ups 2 3 CREATE TABLE "user" ( 4 "username" TEXT NOT NULL, 5 "password" TEXT NOT NULL, 6 "name" TEXT NOT NULL 7 ); 8 9 CREATE TABLE "article" ( 10 "user_id" INT NOT NULL, 11 "content" TEXT NOT NULL 12 ); 13 14 CREATE TABLE "comment" ( 15 "user_id" INT NOT NULL, 16 "article_id" INT NOT NULL, 17 "content" TEXT NOT NULL 18 ); 19 20 # --- !Downs 21 22 DROP TABLE "user"; 23 DROP TABLE "article"; 24 DROP TABLE "comment";
次に各テーブルのモデルを定義する。
/app/models/Models.scala
1 package models 2 3 case class User(username: String, password: String, name: String) 4 case class Article(user_id: Int, content: String) 5 case class Comment(user_id: Int, article_id: Int, content: String)
モデルファイルがエンティティによって分かれていないのがちょっと不思議。
ファイルを別にすることもできると思いますがひとまず例に倣ってこれで。
DAOは下記のように記述。
/app/dao/UserDAO.scala
1 package dao 2 3 import scala.concurrent.Future 4 5 import javax.inject.Inject 6 import models.User 7 import play.api.db.slick.DatabaseConfigProvider 8 import play.api.db.slick.HasDatabaseConfigProvider 9 import play.api.libs.concurrent.Execution.Implicits.defaultContext 10 import slick.driver.JdbcProfile 11 12 class UserDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] { 13 import driver.api._ 14 15 private val Users = TableQuery[UsersTable] 16 17 def all(): Future[Seq[User]] = db.run(Users.result) 18 19 def insert(user: User): Future[Unit] = db.run(Users += user).map { _ => () } 20 21 private class UsersTable(tag: Tag) extends Table[User](tag, "user") { 22 23 def username = column[String]("username") 24 def password = column[String]("password") 25 def name = column[String]("name") 26 27 def * = (username, password, name) <> (User.tupled, User.unapply _) 28 } 29 }
ArticleDAO
, CommentDAO
も同様に定義したが、UserDAO
と殆ど同じコードなので省略。
play consoleを起動しようとする。
activator console -Dplay.evolutions.db.default.autoApply=true
と、ブラウザが開いて下記ページが表示された。
Documentation - Lightbend Activator | @lightbend
To create new Lightbend projects Instead of using the Activator command, make sure you have sbt 0.13.13 (or higher), and use the “sbt new” command, providing the name of the template. For example, “$ sbt new akka/hello-akka.g8”. You can find a list of templates here.
Activatorコマンドは廃止になるらしく、今後はsbtコマンド使ってね、とのこと。
なるほど、Playに関してぐぐってるとactivator run
やらsbt run
やら
同じようなコマンドが出てきていたけど、そういうことだったのか。
というわけで、下記コマンドでコンソールを起動する。
問題なく起動した。
sbt console -Dplay.evolutions.db.default.autoApply=true
User
モデルをnew
してレコード追加してみる。
scala> import play.api._ scala> val env = Environment(new java.io.File("."), this.getClass.getClassLoader, Mode.Dev) scala> val context = ApplicationLoader.createContext(env) scala> val loader = ApplicationLoader(context) scala> val app = loader.load(context) scala> Play.start(app) scala> import dao.UserDAO scala> import models.User scala> import scala.concurrent.Await scala> import scala.concurrent.duration.DurationInt scala> val app2dao = Application.instanceCache[UserDAO] scala> val dao = app2dao(app) scala> val user = new User("zettaittenani", "play", "zettaittenani") scala> Await.result(dao.insert(user), 1.second) scala> Await.result(dao.all, 1.second) res5: Seq[models.User] = Vector(User(zettaittenani,play_test,zettaittenani))
レコードが入ったっぽいのでPostgreSQLの方で確認してみる。
playdb=> select * from user; current_user -------------- play
…どうやらuser
というテーブル名がPostgreSQL自体のユーザのテーブル名とバッティングしていて
追加したレコードがあるかどうかわからない。
命名変更も面倒なのでArticleのレコードで試してみる。
scala> import play.api._ scala> val env = Environment(new java.io.File("."), this.getClass.getClassLoader, Mode.Dev) scala> val context = ApplicationLoader.createContext(env) scala> val loader = ApplicationLoader(context) scala> val app = loader.load(context) scala> Play.start(app) scala> import dao.ArticleDAO scala> import models.Article scala> import scala.concurrent.Await scala> import scala.concurrent.duration.DurationInt scala> val app2dao = Application.instanceCache[ArticleDAO] scala> val dao = app2dao(app) scala> val article = new Article(1, "hogehoge") scala> Await.result(dao.insert(article), 1.second) scala> Await.result(dao.all, 1.second) res2: Seq[models.Article] = Vector(Article(1,hogehoge))
playdb=> select * from article; user_id | content ---------+---------- 1 | hogehoge
よし、入ってる。
明日はview, controllerを使って画面上からレコードのCRUDをやりたい。
Play Scala DB connection その2
昨日の続き。
現状こんなかんじ。
/conf/application.conf
db { slick.dbs.default.driver="slick.driver.PostgresDriver$" slick.dbs.default.db.dataSourceClass = "slick.jdbc.DatabaseUrlDataSource" slick.dbs.default.db.properties.driver = "org.postgresql.Driver" slick.dbs.default.db.user=play slick.dbs.default.db.password="play" }
改めてドキュメントを読んでみる。
ここで、"slick.jdbc.DatabaseUrlDataSource"
のDatabaseUrlDataSource
はべた書きではなく、例えばPostgreSQL
のように書くのでは、と気づく。でもフォーマットがわからないのでどうしようもない。
困っていたらTwitterで救いの手が。。。
私も詳しくない & 全体のコードが解らないのでアレですが...
— YoshinoriN (@yoshinorin24) 2017年9月11日
slick.dbs.default.db.url="jdbc:xxxxx"
の指定がないからじゃないでしょうか?
後はplay.evolutions.enabledがfalseになってるとか...
YoshinoriNさんありがとうございました。
ドキュメントをよくよく読むと、
「Herokuのような特定のプラットフォームでは以下のような書き方もできます」と書いてある。
slick.dbs.default.driver="slick.driver.PostgresDriver$" slick.dbs.default.db.driver="org.postgresql.Driver" slick.dbs.default.db.url=${JDBC_DATABASE_URL}
${JDBC_DATABASE_URL}
にはjdbc:vendor://host:port/db
のようなフォーマットが入るらしい。
これを設定してみる。
db { slick.dbs.default.driver="slick.driver.PostgresDriver$" slick.dbs.default.db.driver="org.postgresql.Driver" slick.dbs.default.db.url="jdbc:postgresql://localhost/playdb" slick.dbs.default.db.user=play slick.dbs.default.db.password="play" play.evolutions.enabled=true }
が、駄目。
どうしようもなく、playのサンプルアプリケーションのapplication.conf
を漁ってみると、
他の人はdb{}
内にslickの設定を書いていないことに気づいた。
それに倣ってslickの設定をdb{}
の外に出してみる。
db{ } slick.dbs.default.driver="slick.driver.PostgresDriver$" slick.dbs.default.db.driver="org.postgresql.Driver" slick.dbs.default.db.url="jdbc:postgresql://localhost/playdb" slick.dbs.default.db.user=play slick.dbs.default.db.password="play" play.evolutions.enabled=true
動いた!!
Apply this script now!
をクリックして、
PostgreSQLにテーブルが入っているかどうか確認する。
$ psql -d playdb -U play -W Password for user play: psql (9.6.3) Type "help" for help. playdb=> \d List of relations Schema | Name | Type | Owner --------+-----------------+-------+------- public | ARTICLE | table | play public | COMMENT | table | play public | member | table | play public | play_evolutions | table | play (4 rows)
ちゃんと入ってる!
調子に乗ってこんな書き方もしてみる。
/conf/application.conf
slick { dbs.default.driver="slick.driver.PostgresDriver$" dbs.default.db.driver="org.postgresql.Driver" dbs.default.db.url="jdbc:postgresql://localhost/playdb" dbs.default.db.user=play dbs.default.db.password="play" } play.evolutions.enabled=true
これも動いた。
かなり苦戦したけどこれでDB接続ができたので、次はModelを設定します。
Model設定ができればアプリケーションが書けるようになるので
今より楽しくなりそう。
Play Scala DB connection
やってみる。
Play * slick * PostgreSQL
設定
/build.sbt
こんなかんじ。
name := """play_test""" version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.7" libraryDependencies ++= Seq( cache, ws, "org.postgresql" % "postgresql" % "42.1.4", "com.typesafe.play" %% "play-slick" % "2.0.2", "com.typesafe.play" %% "play-slick-evolutions" % "2.0.2" )
slick-evolutions
というのは、/conf/evolutions/default
に1.sql
, 2.sql
, … のように
SQL文を書いたファイルをおいておくと、最初にそれを実行してくれるslickのプラグインで、
Playはevolutionsをサポートしているそうです。
Railsのmigrationと違うけどちょっと似てる。
ちなみに最新版のslickを使いたくて以下のように書いていたが、
activator run
が通らなかったので差し替えた。
(PostgreSQL用のJDBCは最新版で大丈夫、っぽい。)
"org.postgresql" % "postgresql" % "42.1.4", "com.typesafe.play" % "play-slick_2.11" % "3.0.1", "com.typesafe.play" % "play-slick-evolutions_2.11" % "3.0.1"
/conf/application.conf
下記を参考に設定。
db { slick.dbs.default.driver="slick.driver.PostgresDriver$" slick.dbs.default.db.dataSourceClass = "slick.jdbc.DatabaseUrlDataSource" slick.dbs.default.db.properties.driver = "org.postgresql.Driver" slick.dbs.default.db.user=play slick.dbs.default.db.password="play" }
DB playdb
は予め作成済み。
localでPostgreSQLを起動し、activator run
してみる。
が、特に反応なし。
evolutions
効かせてるのに起動後に画面に何も出ないし
ログにも何も出てないので連携そのものに失敗しているっぽい。
今日は解決できず、断念。