Sinatra with Vue.js で API と通信するアプリケーションを作ろう Vol.1

これはなに

Sinatra で echo サーバを、 Vue.js でフロントを構築しようというチュートリアルです。
Vol.1 の記事では Sinatra, Vue.js どちらも触れて、 hello world を表示するところまで行います。
「ウェブアプリケーションを作りたいけど何からやっていいかわからない」、「Vue.js やりたいけど、フロントエンド周りがよくわからない」という方向けの記事になります。

入門にしては内容が多めですが、どれも Web開発をするためには便利なツールなので一度自分の手で実行してみることを推奨します。

全てのコードはこちらのリポジトリにあります。
https://github.com/Asuforce/sinatra_with_vue

取り扱うソフトウェア

雑に役割を書いてますが、ここで理解できなくても大丈夫です。
チュートリアルを進めながら学んでいきましょう。わからない部分はまずはリンクしてある公式サイトを見ることがオススメです。
全て 18/3/18 時点の最新のバージョン、シンタックスになるように努力しました。(改善点大歓迎です。

  • Sinatra
    • Webアプリケーションフレームワーク
    • さくっとサーバーサイドが作れます
  • Bundler
    • gemマネージャー
    • gem を管理できます
  • Slim
    • テンプレート言語
    • 楽に html が書けるオススメの gem です
  • RSpec
    • サーバーサイドのテストツール
    • Webアプリケーションの振る舞いをテストします
  • npm
    • jsパッケージマネージャー
    • js のライブラリを管理できます
  • Webpack
    • jsバンドラー
    • js ファイルを任意の設定でまとめてくれます
  • Vue.js
    • jsプログレッシブフレームワーク
    • UI を構築するためのフロントエンドライブラリです

事前準備

以下のソフトウェアを取り扱うので予めインストールしておいてください。バージョンは 18/3/18 のものになります。
あと簡単な Linux コマンドを多用するので、お好きなターミナルを起動しておいてください。

Sinatra でサーバサイドを作る

init

git 管理したい人は、ここで git init しておくと捗ります。

mkdir myapp
cd myapp
bundle init
echo gem \"sinatra\" >> Gemfile
bundle install -j4 --path vendor/bundle
touch app.rb

hello, world

app.rb
require "sinatra/base"

class MyApp < Sinatra::Base
  get "/" do
    'hello, world!'
  end

  run! if app_file == $0 # ファイルを読み込むとサーバを実行する
end
bundle exec ruby app.rb

http://localhost:4567 にアクセスして Hello world! が見えたら正常です。
Mac の場合以下のコマンドが便利です

open http://localhost:4567

git で管理している人は.bundle, venodr.gitignore に追加しましょう。

echo '/.bundle/\n/vendor/' > .gitignore

開発効率を向上させる

ここまでのアプリケーションでローカルサーバを立ち上げてから、 hello, world を変えて再アクセスしてみましょう。レスポンスが hello, world が変更されない筈です。現状はサーバの設定がリロードを実行しないと読み込まれません。

これでは効率が悪いので以下の設定で改善します。

sinatra-contribGemfile に追加します。
sinatra-contrib には他にも便利なライブラリがあるので下記を参考にしてみると発見があると思います。

Sinara-Contribまとめ

echo gem \'sinatra-contrib\' >> Gemfile
bundle install

app.rb に以下を書き加えます。

app.rb
 require "sinatra"
+require 'sinatra/reloader'

 class MyApp < Sinatra::Application
+  configure :development do
+    register Sinatra::Reloader
+  end
+
   get "/" do

これでローカルサーバを起動しながら、ガンガン変更ができるようになります。

Test を書く

簡単なアプリケーションができたので、テストも作成してみましょう。

ref: http://recipes.sinatrarb.com/p/testing/rspec
RSpec を使います。下記を Gemfile に追加します

Gemfile
 gem "sinatra"
 gem 'sinatra-contrib'
+
+group :test do
+  gem 'rspec'
+  gem 'rack-test'
+end

bundle install して rspec --init を実行します。

bundle install
bundle exec rspec --init

spec/spec_helper.rb を下記のように書きましょう。

spec/spec_helper.rb
require 'rack/test'
require 'rspec'

ENV['RACK_ENV'] = 'test'

require File.expand_path '../../app.rb', __FILE__

module RSpecMixin
  include Rack::Test::Methods
  def app
    MyApp
  end
end

RSpec.configure { |c| c.include RSpecMixin }

spec/app_spec.rb を作成しテストを記述します。テストの内容はページのレスポンスと body の内容を確認するものです。

touch spec/app_spec.rb
spec/app_spec.rb
describe "My Sinatra Application" do
  before :all do
    get '/'
  end

  it "should allow accessing the home page" do
    expect(last_response).to be_ok
  end

  it "should discribe message" do
    expect(last_response.body).to eq('hello, world!')
  end
end

テストが書けたら以下を実行して、実際に RSpec を実行してみましょう。
緑色の文字で 2 example, 0 failures と出たら成功です。

bundle exec rspec

View を表示する

Slim というテンプレートエンジンを使います。Ruby には標準ライブラリの ERB を用いる事が多いですが、今回はよりシンプルに記述できる Slim を使って View を作成します。

Slim の記法は以下の記事が大変参考になります。
マークアッパー的 Slim 入門21の手引き

echo gem \"slim\" >> Gemfile
bundle install
mkdir views
touch views/index.slim
vies/index.slim
doctype html
html
  head
    meta charset="UTF-8"
    title MyApp
  body
    div id="app"
      h1 hello, world

同じファイルを html で出力してみましょう。

bundle exec slimrb -p views/index.slim | cat

このような出力結果が得られます。Slim の方がスッキリした印象があると思います。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>MyApp</title>
  </head>
  <body>
    <div id="app">
      <h1>
        hello, world
      </h1>
    </div>
  </body>
</html>

アプリケーションのコードを変更して View ファイルを表示してみましょう。

app.rb
 require "sinatra/reloader"
+require "slim"

 class MyApp < Sinatra::Base
   configure :development do
     register Sinatra::Reloader
   end

   get "/" do
-    'hello, world!'
+    slim :index
   end

H1 タグで大きく表示されるようになっていたら成功です。

データの受け渡し

ここまでで静的なページを作ることができました。
次はアプリケーションからのパラメータを表示する例です。

app.rb
 get "/" do
+  @message = 'This is MyApp'
   slim :index
 end
vies/index.slim
 body
   div id="app"
-    h1 hello, world
+    h1 = @message

ブラウザをリロードして This is MyApp と表示されたら成功です。
このままだとテストが失敗してしまうので、メッセージの内容を正しく書き直してテストをパスするようにします。

spec/app_spec.rb
   it "should discribe message" do
-    expect(last_response.body).to eq('hello, world!')
+    expect(last_response.body).to include('This is MyApp')
   end

再度テストを実行してパスすることを確認してください。

フロントエンドの作成

Vue.js の導入

予め のインストールをお願いします。
Node.js がインストールできたら以下を実行してください。package.json が作成されます。

# package.json を初期化します
npm init -y

# i = install
# -D = devDependencies に追加
npm i -D webpack webpack-cli

# Vue.js のインストール
npm i vue

./node_modules にモジュールがインストールされます。
このディレクトリは ignore します。

echo /node_modules/ >> .gitignore

今回は以下の設定が package.json にあれば十分です。(特にデフォルトから編集する必要はありません。が以下をベースに話を進めます。

package.json
{
  "dependencies": {
    "npm": "^5.7.1",
    "vue": "^2.5.16"
  },
  "devDependencies": {
    "webpack": "^4.1.1",
    "webpack-cli": "^2.0.12"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

webpack の準備

ref: 最新版で学ぶwebpack 4入門 – JS開発のモジュールバンドラ
Vue.js の導入を行なった際に webpack も導入しました。
これは高度な Web アプリケーションの構築のためのモジュールバンドラーです。js, css の minify をビルド時に実行することも可能です。
参考記事がとてもわかりやすいので、併せてご覧ください。

早速、webpack の設定を行いましょう。

touch webpack.config.js

以下の設定で ok です。
entry はデフォルトの src/index.js を使うので書きません。output は Sinatra が js ファイルを読み込む public 以下に作ります。

webpack.config.js
const path = require('path')

module.exports = {
  output: {
    path: path.join(__dirname, 'public', 'js'),
    filename: 'main.js'
  },
  resolve: {
    alias: {
      'vue': 'vue/dist/vue.common.js'
    }
  }
};

ファイルとディレクトリなども作成しておきましょう。

mkdir src
touch src/index.js

mkdir public
touch public/.gitignore

Vue.js を記述する

早速、 Vue.js を使ってデータを表示していきましょう。

index.slim を以下のように設定します。

v-text はテキストコンテンツを表示するためのディレクティブです。よく {{message}} このような mustache 記法が使われる事が多いですが、DOM が構成された後に画面に描画されてしまうのを防ぐために v-text を使っています。
この問題は DOM が描画される前に js ファイルが読み込まれると発生します。footer で読み込んでいるのは header だと先述の問題が発生しやすいためです。知見がある方はこの現象の回避方法をご教授いただければと思います。

views/index.slim
   body
     div id="app"
-      h1 = @message
+      h1 v-text="message"
+  footer
+    script type="text/javascript" src="/js/main.js" charset="utf-8"

js ファイルはこのように記述します

src/index.js
Vue = require('vue')

new Vue({
  el: '#app',
  data: {
    message: 'Hello world from Vue.js'
  },
});

ここまでできたら build をしましょう。npm script を活用します。
--mode は Webpack4 からの機能になり、development, production が選べます。

package.json
     "webpack-cli": "^2.0.11"
   },
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "build": "webpack --mode development"
   }

以下のコマンドを実行して、ビルドを行います。

npm run build

build が終わったら public/js/main.js を確認してみましょう。Vue.js 本体を含む諸々が1個のファイルになってるのがわかります。
確認できたらローカルサーバを立ち上げて確認しましょう。画面に Hello world from Vue.js と表示されたら成功です。

この変更によって再度テストが落ちるようになっています。画面の描画は Vue.js が担うようになったので RSpec のテストの範疇を超えました。実際はフロントエンドテストフレームワークを使ってテストをするのが望ましいですが、今回は割愛させていただきます。

対象の部分を削除して、テストを通します。

spec/app_spec.rb
   it "should allow accessing the home page" do
     expect(last_response).to be_ok
   end
-
-  it "should discribe message" do
-    expect(last_response.body).to include('This is MyApp')
-  end
 end

まとめ

ここまでの作業で、Webアプリケーションの仕組みに触れながら、最新の環境を作成できるようになりました。
ここで作ったアプリケーションを使って自分なりの工夫をしてみるとより理解が深まると思います。

Vol.2 ではエコーサーバやリッチなフォームの作成を行う予定です。お楽しみに!

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.