Railsもどきを作ってみよう２(コントローラー編)

# はじめに
この記事は前回の続きです。↓↓↓
https://qiita.com/alex0033/items/73f62ac7e5700ab2b4d6

今回は、コントローラー編ということで、Railsにおけるルーティングを実装していきたいと思います。

目指すべきディレクトリ構成は下記の通りです。

```
directory
  |- app
      |- controllers
           |- basic_pages_controller
      |- views
           |- basic_pages
               |- home.html.erb # ホームページ
               |- next.html.erb # ネクストページ
  |- config
       |- routes.rb
  |- srv.rb
```
# ポイント
前回まで、次のような状態になっています。

・```ruby srv.rb```とターミナルで入力すると、サーバを立てることができる。
・http://localhost:3000 にアクセスすると、ホームページが表示される。

今回目指すのは、下記の通りです。

・http://localhost:3000 (以後、ルートパス)にアクセスすると、ホームページが表示される。
・http://localhost:3000/next (以後、ネクストパス)にアクセスすると、ネクストページが表示される。
・ページアクセスの前にコントローラーを通るようにする

そこで、本記事の説明は次のようになります。

１．ネクストページの表示
　①ビューファイル(app/views/basic_pages/next.html.erb)の作成
　②<em>\<a>タグ</em>でホームページからアクセスしてみる
　③設定(srv.rb)を変更
２．コントローラーもどきの作成
　①サーブレットによるロジックの反映
　②ファイルを整理してコントローラー風にする
３．ルーティング
　①ルーティングの設定ファイル(config/routes.rb)の作成
　②メタプログラミングによるルーティングの自動生成


# １．ネクストページの表示
まずは、ネクストページを作成し、表示出来るようにしましょう。次の順に記述してきます。

①ビューファイル(app/views/basic_pages/next.html.erb)の作成
②<em>\<a>タグ </em>でホームページからアクセスしてみる
③設定(srv.rb)を変更

## ①ビューファイル(app/views/basic_pages/next.html.erb)の作成
ビューファイルを作成しましょう。

```erb:app/views/basic_pages/next.html.erb
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>ネクストページ</h1>
    <p>This is a next page.</p>
  </body>
</html>
```
##②<em>\<a>タグ </em>でホームページからアクセスしてみる
ビューファイルの存在だけでは、アクセスすることができません。そこで、ホームページから<em>\<a>タグ </em>を用いてアクセスを試みてみましょう。

```erb:app/views/basic_pages/home.html.erb
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>ホームページ</h1>
    <p>Hello, World</p>
    <%= 1 + 4 %>

    <!-- ネクストページへのリンク -->
    <a href="../next.html.erb">to next</a>
  </body>
</html>
```
ルートパスへアクセスすると、次のようになっているはずです。
![Screenshot from 2020-11-06 11-56-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642882/05a5fb65-1c02-d1b8-8611-200fcbf5577a.png)
試しに"to next"をタッチしてリンクをたどってみましょう。

おそらく、URLがhttp://localhost:3000/next.html.erb となっただけではないでしょうか。
## ③設定(srv.rb)を変更
ここまで、```DocumentRoot```として、 ホームページへのパスを設定していました。

ここからは、```app/views```に設定し、個別にURLと表示ファイルを設定していきます。

```rb:srv.rb
require 'webrick'

op = {
    BindAdress: "127.0.1",
    Port: 3000,
    DocumentRoot: "."
}

WEBrick::HTTPServlet::FileHandler.add_handler("erb", WEBrick::HTTPServlet::ERBHandler)
s = WEBrick::HTTPServer.new(op)

# 以下、URLと表示ファイルの個別設定
s.mount('/', WEBrick::HTTPServlet::FileHandler, 'app/views/basic_pages/home.html.erb')
s.mount('/next', WEBrick::HTTPServlet::FileHandler, 'app/views/basic_pages/next.html.erb')

s.start
```
これで、ルートパスとネクストパスの設定はできました。

しかし、ホームページとのリンクが未設定なので、設定しましょう。

```erb:app/views/basic_pages/home.html.erb
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>ホームページ</h1>
    <p>Hello, World</p>
    <%= 1 + 4 %>
    <!-- ネクストページへのリンク設定 -->
    <a href="/next">to next</a>
  </body>
</html>
```
```erb:app/views/basic_pages/next.html.erb
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>ネクストページ</h1>
    <p>This is a next page.</p>

    <!-- ホームページへのリンク設定 -->
    <a href="/">to home</a>
  </body>
</html>
```

これで、"to next"や"to home"をクリックすることで、ホームページとネクストページへの行き来ができるようになりました。

参考↓↓↓
https://docs.ruby-lang.org/ja/latest/library/webrick.html
https://docs.ruby-lang.org/ja/latest/method/WEBrick=3a=3aHTTPServer/i/mount.html

# ２．コントローラーの作成
ここまでで、URLを設定することによる画面遷移ができるようになりました。

ここから、コントローラーを通った上で、ビューを表示できるようにしていきましょう。

①サーブレットによるロジックの反映
②ファイルを整理してコントローラー風にする

## ①サーブレットによるロジックの反映
まずは、サーブレットを使って、ページ遷移前にロジックが反映されるようにしてみましょう。

```rb:srb.rb
require 'webrick'
require 'erb'

include WEBrick

op = {
    BindAdress: "127.0.1",
    Port: 3000,
    DocumentRoot: "."
}

s = HTTPServer.new(op)

# サーブレット
class HomeServlet < HTTPServlet::AbstractServlet
    def do_GET(req, res)
        # ロジックのスタート
        @home_string = "pass homeServlet"
        # ロジックのエンド
        erb = ERB.new File.open('app/views/basic_pages/home.html.erb').read
        res.body = erb.result(binding)
    end
end

class NextServlet < HTTPServlet::AbstractServlet
    def do_GET(req, res)
        # ロジックのスタート
        @next_string = "pass nextServlet"
        # ロジックのエンド
        erb = ERB.new File.open('app/views/basic_pages/next.html.erb').read
        res.body = erb.result(binding)
    end
end

s.mount('/', HomeServlet)
s.mount('/next', NextServlet)
trap(:INT){ s.shutdown }
s.start
```
これで、インスタンス変数の値が反映されるはずです。

```erb:app/views/basic_pages_controller.rb
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>ホームページ</h1>
    <p>Hello, World</p>
    <p><%= 1 + 4 %></p>
    <a href="/next">to next</a>
    <p><%= @home_string %></p>
    <p><%= @next_string %></p>
  </body>
</html>
```
サーバを立てホームページにアクセスすると、インスタンス変数に格納された"pass homeServlet"という文字列の表示を確認することができると思います。

参考↓↓↓
http://unageanu.hatenablog.com/entry/20080720/1216539411
https://magazine.rubyist.net/articles/0017/0017-BundledLibraries.html

## ②ファイルを整理してコントローラー風にする
このままでは、コントローラーに当たるロジックがsrv.rbに書かれており、見通しが悪くなっています。

そこで、新たなディレクトリとファイルを作ることで整理していきましょう。

```rb:app/controllers/basic_apges_cotroller.rb
require 'erb'

def render_result(path)
    erb = ERB.new File.open("app/views/#{path}.html.erb").read
    erb.result(binding)
end

module BasicPagesController
    class HomeServlet < HTTPServlet::AbstractServlet
        def do_GET(req, res)
            @home_string = "pass homeServlet"
            res.body = render_result 'basic_pages/home'
        end
    end

    class NextServlet < HTTPServlet::AbstractServlet
        def do_GET(req, res)
            @next_string = "pass nextServlet"
            res.body = render_result 'basic_pages/next'
        end
    end
end
```
```rb:srv.rb
require 'webrick'
include WEBrick

op = {
    BindAdress: "127.0.1",
    Port: 3000,
    DocumentRoot: "."
}

s = HTTPServer.new(op)

# ホームページとネクストページを表示させるためのロジック
require './app/controllers/basic_pages_controller.rb'
include BasicPagesController
s.mount('/', HomeServlet)
s.mount('/next', NextServlet)

trap(:INT){ s.shutdown }
s.start
```
これで、だいぶスッキリしました。

# ３．ルーティング
ここまでで、コントローラーを通ったページ表示が実装できました。

しかし、このままでは、新たなページ作成の度に、srv.rbを手動で更新することになります。

それでは、どんなページがあるのか分かりにくいので、ルーティングファイル(config/routes.rb)を使って整理します。

以下の手順で実装していきます。

①ルーティングの設定ファイル(config/routes.rb)の作成
②メタプログラミングによるルーティングの自動生成

## ①ルーティングの設定ファイル(config/routes.rb)の作成
```rb:config/routes.rb
class Routes
    def initialize
        @routes = Array.new
    end

    def make_routes
        # 下記にルートを記載
        get "/", "basic_pages", "home"
        get "/next", "basic_pages", "next"

        # 下記に返り値を記載(ルート情報の一覧を返す)
        return @routes
    end

    def get(url, controller_name, action)
        @routes.push({ url: url, controller_name: controller_name, action: action }) 
    end
end
```
srv.rbにおいて、インスタンスを生成して、make_routesでルート一覧を生成・取得できるようにします。

## ②メタプログラミングによるルーティングの自動生成
config/routes.rbを元に、実際のルーティングをsrv.rbで設定していくコードを書く必要があります。

```rb:srv.rb
require 'webrick'
include WEBrick

op = {
    BindAdress: "127.0.1",
    Port: 3000,
    DocumentRoot: "."
}

s = HTTPServer.new(op)

# Stringクラスの拡張
class String
    def to_camel()
        self.split("_").map{ |w| w[0] = w[0].upcase; w }.join
    end
end

# ルーティング情報の取得
require './config/routes.rb'
r = Routes.new
routes = r.make_routes

# メタプログラミングでルーティングを自動生成
routes.each do |route|
    require "./app/controllers/#{route[:controller_name]}_controller.rb"
    include eval("#{route[:controller_name].to_camel}Controller")

    s.mount route[:url], eval("#{route[:action].to_camel}Servlet")
end

trap(:INT){ s.shutdown }
s.start
```

これで、ルーティングも完成です。

# まとめ
今回はコントローラー編ということで、コントローラーを通ったページ遷移を実装しました。

railsっぽくするには、まだまだ道のりが長そうですが、少しずつ実装していきたいと思います。