i-Vinci TechBlog
株式会社i-Vinciの技術ブログ

SinatraでWEBapp

こんにちわ。i-Vinciのakiyoshiです。
故あってローカル環境に簡易WEBアプリを立てる必要があり、その時の手順をままこちらに残します。素晴らしきネタの再利用!
Rubyの環境構築からSinatraの立ち上げまで行っているため、手順書の如き内容になってしまいました。
ちなみに今回はDBを使っていないため、そのあたりの手順は抜けています。

開発環境整え

OS周り

vagrant up直後からスタートしています。

  • CentOS7
# カーネルを最新化
$ sudo yum -y update kernel
$ sudo yum -y update
# 今回は個人開発であるため、セキュリティオフ
$ sudo sed -i "s/(^SELINUX=).*/disabled/" /etc/selinux/config
# 必要なパッケージインストール
$ sudo yum -y install kernel-devel kernel-headers dkms gcc gcc-c++ cmake git openssl-devel readline-devel zlib-devel libicu-devel

Ruby関連

今回は複数バージョンのRubyを簡単にスイッチできるよう、rbenvでRubyのインストールを行います。

# rbenv(Rubyバージョン管理ツール)準備
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ rbenv --version
> rbenv 1.2.0-14-gc6cc0a1
# rbenvプラグイン(rbenvで複数バージョンのrubyをインストールする際に必要)
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
# 物が入っているかの確認
$ ls ~/.rbenv/plugins/ruby-build
> bin  CODE_OF_CONDUCT.md  CONTRIBUTING.md  install.sh  LICENSE  README.md  script  share  test
# 使いたいバージョンのRubyをインストール
$ rbenv install --list
> 2.6.10
> 2.7.6
> 3.0.4
> 3.1.2
> jruby-9.3.4.0
> mruby-3.0.0
> rbx-5.0
> truffleruby-22.0.0.2
> truffleruby+graalvm-22.0.0.2
> 
> Only latest stable releases for each Ruby implementation are shown.
> Use 'rbenv install --list-all / -L' to show all local versions.
rbenv install 3.1.1
# 使うバージョンを指定(globalならシステム全体、localならカレントディレクトリ配下)
rbenv global 3.1.1
rbenv rehash
ruby -v
> ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
# bundleでrubyのバージョンごとにgemを管理
gem install bundler
bundle -v
> Bundler version 2.3.11

ここから開発

やっとこそさ、実際のアプリ雛型(=いわゆるHello, world)作成に入ります。

$ mkdir app
$ cd app
$ bundle init
> Writing new Gemfile to /vagrant/sinatra_project/app/Gemfile
$ bundle add sinatra --skip-install
$ bundle add sinatra-contrib --skip-install
# ruby2.x系なら不要
$ bundle add webrick --skip-install
# 必要なgemをGemfileに記載
$ vi Gemfile

この時点でのGemfile

# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem "sinatra", "~> 2.2"
gem "sinatra-contrib", "~> 2.2"
gem "webrick", "~> 1.7"
# Viewでhamlを使うために。erbで良ければ不要。
gem 'haml-rails'
$ bundle install --path vendor/bundle
$ cd app/
$ touch app.rb
$ vi app.rb 

app.rbは以下で作成

require "sinatra"
# サーバー立ち上げ状態ままでソースの変更を反映するために必要。
require "sinatra/reloader"

set :environment, :production
configure :production do
  enable :reloader
end

get '/' do
  'indexpage'
end

基本的な準備ができたため、やっと実行。
Sinatraのデフォルトポートは4567です。

$ bundle exec ruby app.rb

http://localhost:4567/

この時点でのディレクトリ構成

.
├── app
│   └── app.rb
├── Gemfile
├── Gemfile.lock
└── vendor
    └── bundle
        └── ruby
            └── rubyのバージョンごとにgemが格納

いろいろと追加

上のままでは寂しいので少し手を加えていきます。SinatraはMVCベースの考えにはなっていませんが、
便宜的に以下コントローラ層、ビュー層と表現しておきます。

コントローラ層

小規模なうちはコントローラ処理をapp.rbに集約します。
規模が大きくなってくると見通しが悪くなりがちなので分割したほうがよいですが、
そのレベルになってくるとRailsでもいいのでは?と考えてしまいます。

require "sinatra"
require "sinatra/reloader"

set :environment, :production
configure :production do
  enable :reloader
end

get '/' do
# 以下がそのまま戻され画面に表示される
  'indexpage'
end

get '/input' do
# Viewはhaml形式で指定(erbやmarkdownなどかなりの種類を利用可能)
  haml :input_form
end

post '/confirm' do
# modelなど使わず、素で書いた場合
  @sender_name = params[:sender_name]
  @age = params[:age]
  @free_form = params[:free_form]
  haml :confirm
end

post '/complete' do
  if params[:btn_value] == "submit"
    haml :complete
  else
    haml :input_form
  end
end

ビュー層

hamlは、rubyで使うテンプレートエンジンの1つで、インデントを使ってビューを表現します。
Rubyでメジャーどころはerbだと思うのですが、個人的にはJSPっぽくて少し苦手なので、こちらを触ってみました。
実際に書いてみると下のような感じで結構独特で面白いです。
※ドキュメントを読む限りだと、もっとすっきり書けるはずです。

!!!
%head
  %title INPUT PAGE
  %link(rel="stylesheet" href="css/styles.css")
%body
  %h1 input page
  %form{:action => '/confirm', :method => 'post'}
    %p
      %label 名前:
    %p
      %input{:name => "sender_name", :type => "text"}
    %p  
      %label 年代:
    %p 
      %select{:name => "age", :size => "1"}
        %option{:value => ""} --
        %option{:value => "10代"} 10代
        %option{:value => "20代"} 20代
        %option{:value => "30代"} 30代
        %option{:value => "40代"} 40代
        %option{:value => "50代"} 50代
        %option{:value => "60代以上"} 60代以上
    %p  
      %label 自由記述欄:
    %p  
      %textarea{:name => "free_form", :type => "text"}
    %p  
      %input{:class => "btn", :type => "submit", :value => "confirm"}

最終的なディレクトリ構成

今回はここまでですが、
ルーティングが複雑になってくれば、config.ruを足したり、
DBを使うならmodelを作成した方がいいでしょうし、
ビューで複雑なものを表示したいならhelperを作ったりetc, etc.
まだまだ手を加える余地はありますね。

.
├── app
│   ├── app.rb
│   ├── public # cssやjsはここ
│   │   └── css
│   │       └── styles.css
│   └── views # ビューファイルはここ
│       ├── complete.haml
│       ├── confirm.haml
│       └── input_form.haml
├── Gemfile
├── Gemfile.lock
└── vendor
    └── bundle
        └── ruby
            └── rubyのバージョンごとにgemが格納

まとめ

最近、環境周りについてはお膳立てされていることも多かったので、久しぶりに四苦八苦。
いろいろバージョンいじったり、好き勝手している最中にこのあたりで沼りました。こればっかりは何度やっても楽しくない。。。
それはともかく、久しぶりにRubyを触っていて『もう少し時間を作って、DB追加してActiveRecord使ったり、ビューテンプレート片っ端から使ってみたりしたいなぁ。』と思ったのでした。
でも、JavaScript系もやらないとなぁ。

参考