From 98a27e9e23772fadff948414eebb73c20bd4fa69 Mon Sep 17 00:00:00 2001 From: ucdmsky Date: Sat, 30 Sep 2017 21:11:11 +0900 Subject: [PATCH 1/6] add msky026 --- msky026/README.md | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 msky026/README.md diff --git a/msky026/README.md b/msky026/README.md new file mode 100644 index 0000000..1ddbd6e --- /dev/null +++ b/msky026/README.md @@ -0,0 +1,200 @@ +Title: CrystalとWeb 2 +Author: msky +Twitter: @msky026 + +# はじめに + +1年前の技術書典1で記述しました、「CrystalとWeb」からの変更点を主に纏めます。 +内容は以下のとおりです。 + +- Kemalの変更点 +- Sidekiq.crと組み合わせて非同期処理を実現する + +# Kemalの変更点 + +## DB接続設定の変更 + +まず主だった変更点として、DB接続を行うモジュールが変更になっています。 +以前はPostgreSQLを使用する際はkemal-pgを使ってDBに接続していましたが、DB接続関連は[crystal-db](https://github.com/crystal-lang/crystal-db)のライブラリがデファクトスタンダードになっています。こちらのライブラリは現時点でコネクションプールも備えております。 +以前はコネクションプール使用時には専用のライブラリを使用していましたがそれも不要になりました。 + +以下に変更点について記載していきます。変更点のみの記述となりますが、全体像が見たい方は、[kemal-sample](https://github.com/msky026/kemal-sample)を参照してみてください。 +また、本サンプルは全てCrystal ver 0.23.0で実行しております。 + +`shard.yml`ファイルを以下の内容に修正します。 + +``` +name: msky-kemal-sample +version: 0.2.0 + +dependencies: + kemal: + github: sdogruyol/kemal + branch: master + +dependencies: + crystal-db: + github: crystal-lang/crystal-db + branch: master +``` + +修正後に以下のコマンドを実行します。 + +``` +shards update +``` + +続いてソースの修正を行っていきます。 + +`src/kemal-sample.cr` +```crystal +require "kemal" +require "db" +require "pg" + +database_url = if ENV["KEMAL_ENV"]? && ENV["KEMAL_ENV"] == "production" + ENV["DATABASE_URL"] +else + "postgres://preface@localhost:5432/kemal_sample" +end + +db = DB.open(database_url) + +["/", "/articles"].each do |path| + get path do |env| + articles = [] of Hash(String, String | Int32) + db.query("select id, title, body from articles") do |rs| + rs.each do + article = {} of String => String | Int32 + article["id"] = rs.read(Int32) + article["title"] = rs.read(String) + article["body"] = rs.read(String) + articles << article + end + end + db.close + render "src/views/index.ecr", "src/views/application.ecr" + end +end + +get "/articles/new" do |env| + render "src/views/articles/new.ecr", "src/views/application.ecr" +end + +post "/articles" do |env| + title_param = env.params.body["title"] + body_param = env.params.body["body"] + params = [] of String + params << title_param + params << body_param + db.exec("insert into articles(title, body) values($1::text, $2::text)", params) + db.close + env.redirect "/" +end + +get "/articles/:id" do |env| + articles = [] of Hash(String, String | Int32) + article = {} of String => String | Int32 + id = env.params.url["id"].to_i32 + params = [] of Int32 + params << id + article["id"], article["title"], article["body"] = db.query_one("select id, title, body from articles where id = $1::int8", params, as: {Int32, String, String}) + articles << article + db.close + render "src/views/articles/show.ecr", "src/views/application.ecr" +end + +get "/articles/:id/edit" do |env| + articles = [] of Hash(String, String | Int32) + article = {} of String => String | Int32 + id = env.params.url["id"].to_i32 + params = [] of Int32 + params << id + article["id"], article["title"], article["body"] = db.query_one("select id, title, body from articles where id = $1::int8", params, as: {Int32, String, String}) + articles << article + db.close + render "src/views/articles/edit.ecr", "src/views/application.ecr" +end + +put "/articles/:id" do |env| + id = env.params.url["id"].to_i32 + title_param = env.params.body["title"] + body_param = env.params.body["body"] + params = [] of String | Int32 + params << title_param + params << body_param + params << id + db.exec("update articles set title = $1::text, body = $2::text where id = $3::int8", params) + db.close + env.redirect "/articles/#{id}" +end + +delete "/articles/:id" do |env| + id = env.params.url["id"].to_i32 + params = [] of Int32 + params << id + db.exec("delete from articles where id = $1::int8", params) + db.close + env.redirect "/" +end + +Kemal.run + +``` + +主だった変更箇所について解説します。まずクエリは以下のように記述します。 + +```crystal +db.query("select id, title, body from articles") do |rs| + rs.each do + article = {} of String => String | Int32 + article["id"] = rs.read(Int32) + article["title"] = rs.read(String) + article["body"] = rs.read(String) + articles << article + end +end +``` + +queryメソッドにSQLクエリを記述し、ループ内で結果を格納していきます。 +1件だけ取得したい場合は`query_one`もしくは`query_one?`を使います。後者は1件もデータが無い場合がありうる場合に使います。 + +```crystal +article["id"], article["title"], article["body"] = db.query_one("select id, title, body from articles where id = $1::int8", params, as: {Int32, String, String}) +``` + +query_oneの戻り値は、asで指定した型のToupleになります。 +上記の場合ですと、`Touble(Int32, String, String)`になります。 + +updateやdeleteの場合は`exec`メソッドを使用します。 + +```crystal +db.exec("update articles set title = $1::text, body = $2::text where id = $3::int8", params) +``` + +基本的にはあまり変更はない箇所です。 + + +続いて画面側も一部修正します。 +具体的には`article["id"]?`のようにnilの場合の修正です。(Crystalの仕様変更に伴う修正) + +`src/views/index.ecr` +```crystal +

Article List

+ + + + + + + <% articles.each do |article| %> + + + + <% end %> + +
title
" target="_top"><%=article["title"]? %>
+``` + +## Sidekiq.crと組み合わせて非同期処理を実現する + From 64d94f2281b4cdb1a247423f6c512adeef4e5029 Mon Sep 17 00:00:00 2001 From: msky026 Date: Sat, 30 Sep 2017 21:23:50 +0900 Subject: [PATCH 2/6] modify typo --- msky026/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/msky026/README.md b/msky026/README.md index 1ddbd6e..6dce57a 100644 --- a/msky026/README.md +++ b/msky026/README.md @@ -24,7 +24,7 @@ Twitter: @msky026 `shard.yml`ファイルを以下の内容に修正します。 ``` -name: msky-kemal-sample +name: kemal-sample version: 0.2.0 dependencies: @@ -198,3 +198,11 @@ db.exec("update articles set title = $1::text, body = $2::text where id = $3::in ## Sidekiq.crと組み合わせて非同期処理を実現する +Crystalで非同期処理を行いたい場合は、Sidekiqを使用することである程度可能になります。 +Rubyでよく使用されるSidekiqのCrystal版があります。 + +[Sidekiq.cr](https://github.com/mperham/sidekiq.cr) + + + + From 336481b31bc22a2e5086044acabacbc48cb5625e Mon Sep 17 00:00:00 2001 From: msky026 Date: Sat, 30 Sep 2017 21:47:05 +0900 Subject: [PATCH 3/6] =?UTF-8?q?sidekiq=E3=81=AF=E4=BB=8A=E5=9B=9E=E6=89=B1?= =?UTF-8?q?=E3=82=8F=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msky026/README.md | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/msky026/README.md b/msky026/README.md index 6dce57a..05c8bf7 100644 --- a/msky026/README.md +++ b/msky026/README.md @@ -4,17 +4,14 @@ Twitter: @msky026 # はじめに -1年前の技術書典1で記述しました、「CrystalとWeb」からの変更点を主に纏めます。 -内容は以下のとおりです。 - -- Kemalの変更点 -- Sidekiq.crと組み合わせて非同期処理を実現する +1年前の技術書典1で「CrystalとWeb」について記述しました。 +CrystalやKemalもそれから多くの点で変更になりましたが、本稿では主にDBの扱い方について解説します。 # Kemalの変更点 ## DB接続設定の変更 -まず主だった変更点として、DB接続を行うモジュールが変更になっています。 +まず主だった変更点として、DB接続を行うモジュールが変更されましt。 以前はPostgreSQLを使用する際はkemal-pgを使ってDBに接続していましたが、DB接続関連は[crystal-db](https://github.com/crystal-lang/crystal-db)のライブラリがデファクトスタンダードになっています。こちらのライブラリは現時点でコネクションプールも備えております。 以前はコネクションプール使用時には専用のライブラリを使用していましたがそれも不要になりました。 @@ -33,7 +30,7 @@ dependencies: branch: master dependencies: - crystal-db: + db: github: crystal-lang/crystal-db branch: master ``` @@ -196,13 +193,3 @@ db.exec("update articles set title = $1::text, body = $2::text where id = $3::in ``` -## Sidekiq.crと組み合わせて非同期処理を実現する - -Crystalで非同期処理を行いたい場合は、Sidekiqを使用することである程度可能になります。 -Rubyでよく使用されるSidekiqのCrystal版があります。 - -[Sidekiq.cr](https://github.com/mperham/sidekiq.cr) - - - - From 492912e5383241c83d7a2197dac204464533e302 Mon Sep 17 00:00:00 2001 From: msky026 Date: Sun, 1 Oct 2017 16:22:24 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E3=82=BB=E3=83=83=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E7=B3=BB=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msky026/README.md | 131 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/msky026/README.md b/msky026/README.md index 05c8bf7..6f8f654 100644 --- a/msky026/README.md +++ b/msky026/README.md @@ -5,9 +5,13 @@ Twitter: @msky026 # はじめに 1年前の技術書典1で「CrystalとWeb」について記述しました。 -CrystalやKemalもそれから多くの点で変更になりましたが、本稿では主にDBの扱い方について解説します。 +そこでKemalを用いて簡単なCRUD機能を持つミニブログを作成しました。 +作成物については[kemal-sample](https://github.com/msky026/kemal-sample)を参照してみてください。 +そこからCrystalやKemalもそれから多くの点で変更になりました。 +本稿では前回作成した箇所からの変更点から以下の内容について解説します。 -# Kemalの変更点 +- DBの扱い方について +- セッションについて ## DB接続設定の変更 @@ -15,7 +19,7 @@ CrystalやKemalもそれから多くの点で変更になりましたが、本 以前はPostgreSQLを使用する際はkemal-pgを使ってDBに接続していましたが、DB接続関連は[crystal-db](https://github.com/crystal-lang/crystal-db)のライブラリがデファクトスタンダードになっています。こちらのライブラリは現時点でコネクションプールも備えております。 以前はコネクションプール使用時には専用のライブラリを使用していましたがそれも不要になりました。 -以下に変更点について記載していきます。変更点のみの記述となりますが、全体像が見たい方は、[kemal-sample](https://github.com/msky026/kemal-sample)を参照してみてください。 +以下に変更点について記載していきます。変更点のみの記述となりますが、全体像が見たい方は、上記でも記載していますが、[kemal-sample](https://github.com/msky026/kemal-sample)を参照してみてください。 また、本サンプルは全てCrystal ver 0.23.0で実行しております。 `shard.yml`ファイルを以下の内容に修正します。 @@ -172,8 +176,8 @@ db.exec("update articles set title = $1::text, body = $2::text where id = $3::in 基本的にはあまり変更はない箇所です。 -続いて画面側も一部修正します。 -具体的には`article["id"]?`のようにnilの場合の修正です。(Crystalの仕様変更に伴う修正) +続いて画面側も一部修正します。 +具体的には`article["id"]?`のようにnilの場合の修正です。(Crystalの仕様変更に伴う修正) `src/views/index.ecr` ```crystal @@ -193,3 +197,120 @@ db.exec("update articles set title = $1::text, body = $2::text where id = $3::in ``` +## セッションについて + +Kemalではセッションの機能を標準でサポートします。 +下記ファイルを編集し、`shards update`を実行します。 + +`shard.yml` +``` +dependencies: + kemal-session: + github: kemalcr/kemal-session +``` + +本サンプルでは、簡単な認証機能を追加してみます。新規投稿を行う場合は認証済みのユーザでなければ出来ない(新規投稿画面に遷移できない)ようにします。 + +ソースを以下の内容で修正します。 + +`src/kemal-sample.cr` +```crystal +require "kemal" +require "kemal-session" +(中略) + +Kemal::Session.config do |config| + config.cookie_name = "session_id" + config.secret = "some_secret" + config.gc_interval = 2.minutes # 2 minutes +end + +def authorized?(env) + env.session.string?("username") +end + +get "/login" do |env| + render "src/views/login.ecr", "src/views/application.ecr" +end + +post "/login" do |env| + user_id_param = env.params.body["user_id"] + password_param = env.params.body["password"] + if user_id_param == "user1" && password_param == "pass1" + env.session.string("username", "user1") + env.redirect "/" + else + env.redirect "/login" + end +end + +get "/logout" do |env| + env.session.destroy + env.redirect "/" +end +``` + +`src/views/application.ecr` +``` + + + + + kemal sample + + + + + +
+ <%= content %> +
+ + +``` + +主な設定箇所について解説します。まずセッションの設定を行います。   +```crystal +Kemal::Session.config do |config| + config.cookie_name = "session_id" + config.secret = "some_secret" + config.gc_interval = 2.minutes # 2 minutes +end +``` + +本サンプルではcookieにセッションを保存します。`cookie_name`と`secret`でcookieの設定を行います。 +`gc_interval`で有効期間を設定します。デフォルトでは4分です。 +その他の設定は[kemal-session](https://github.com/kemalcr/kemal-session)を参照してみてください。 + +その他追記箇所についてはログイン、ログアウトのパスを新規で作っています。ユーザIDとパスワードの組み合わせが正しい場合はセッションを作り、そうでない場合はリダイレクトします。 +本運用を考える場合は、設定をDBに持たせるなどします。 + +画面の方を以下の内容に修正します。 + +``` +
  • ArticleList
  • + <% if env.session.string?("username") %> +
  • 新規投稿
  • +
  • ログアウト
  • + <% else %> +
  • ログイン
  • + <% end %> +``` + +セッションの有無で表示を切り分けます。 +本稿では扱いませんが、その他ライブラリを別途使用することでRedisにセッションを保持することも可能になります。   \ No newline at end of file From 5994a28e68d2f8cb448f8ea5f1bcfadd32dd6775 Mon Sep 17 00:00:00 2001 From: msky026 Date: Sun, 1 Oct 2017 16:30:26 +0900 Subject: [PATCH 5/6] fix typo --- msky026/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msky026/README.md b/msky026/README.md index 6f8f654..cf34014 100644 --- a/msky026/README.md +++ b/msky026/README.md @@ -7,15 +7,15 @@ Twitter: @msky026 1年前の技術書典1で「CrystalとWeb」について記述しました。 そこでKemalを用いて簡単なCRUD機能を持つミニブログを作成しました。 作成物については[kemal-sample](https://github.com/msky026/kemal-sample)を参照してみてください。 -そこからCrystalやKemalもそれから多くの点で変更になりました。 -本稿では前回作成した箇所からの変更点から以下の内容について解説します。 +それからCrystalやKemalも多くの点で変更になりました。 +本稿では前回作成した箇所からの変更点のうち以下の内容について解説します。 - DBの扱い方について - セッションについて ## DB接続設定の変更 -まず主だった変更点として、DB接続を行うモジュールが変更されましt。 +まず主だった変更点として、DB接続を行うモジュールが変更されました。 以前はPostgreSQLを使用する際はkemal-pgを使ってDBに接続していましたが、DB接続関連は[crystal-db](https://github.com/crystal-lang/crystal-db)のライブラリがデファクトスタンダードになっています。こちらのライブラリは現時点でコネクションプールも備えております。 以前はコネクションプール使用時には専用のライブラリを使用していましたがそれも不要になりました。 From 7c61390a3e85f5b9e9a730090801da6f1fbd18b7 Mon Sep 17 00:00:00 2001 From: msky026 Date: Sun, 1 Oct 2017 16:33:21 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E8=A7=A3=E8=AA=AC=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msky026/README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/msky026/README.md b/msky026/README.md index cf34014..eb5928b 100644 --- a/msky026/README.md +++ b/msky026/README.md @@ -211,7 +211,8 @@ dependencies: 本サンプルでは、簡単な認証機能を追加してみます。新規投稿を行う場合は認証済みのユーザでなければ出来ない(新規投稿画面に遷移できない)ようにします。 -ソースを以下の内容で修正します。 +ソースを以下の内容で修正します。 +kemal-sample.crは以下の通りです。 `src/kemal-sample.cr` ```crystal @@ -250,6 +251,25 @@ get "/logout" do |env| end ``` +ログイン画面を新規で追加します。 + +`src/views/login.ecr` +``` +

    ログイン

    +
    + +
    +
    + +
    +
    + +
    + +``` + +ヘッダの内容を変更します。 + `src/views/application.ecr` ``` @@ -302,6 +322,7 @@ end 画面の方を以下の内容に修正します。 +`src/views/application.ecr`より抜粋。 ```
  • ArticleList
  • <% if env.session.string?("username") %>