自分用めも。
予め書いておく。
Sinatraで画面をもつ&ログインが必要な&Herokuデプロイ可能なアプリケーションを書くのは結構めんどくさいので、たとえ単機能であってもRailsを使ったほうがいい。間違いない。
前提
フォーム入力したらそれに沿って外部サービスのAPIを叩きに行って、結果を表示するだけのアプリ。一応ActiveRecordは使用する。
ただし社内サービスなので、フォームバリデーションとかは適当でいい。あとは
- Docker, docker-composeで開発環境はサッと作れるのがよい。
- Herokuにデプロイしたい。
- GitHub認証で、なおかつ知らないユーザは弾きたい。
開発用のDockerファイルを作る。
Railsに比べると依存は少ない。
version: "3"
services:
web:
build: .
volumes:
- .:/sinatra-inhouse-app
- ruby-bundle:/usr/local/bundle
working_dir: /sinatra-inhouse-app
volumes:
ruby-bundle:
driver: local
FROM ruby:2.7-alpine
Gemfileを作る
$ docker-compose build
$ docker-compose run --rm web sh
/sinatra-inhouse-app # bundle init
SinatraでHello Worldするまで
このへんまでは楽勝。
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "sinatra", require: false
↑sinatraに require: false
をつけないと、at_exitフックでサーバーが起動してしまうので、地味にハマる。注意。
require 'sinatra/base'
class InhouseApp < Sinatra::Base
get '/' do
'<h1>It works!</h1>'
end
end
require "bundler/setup"
Bundler.require
require_relative './inhouse_app'
run InhouseApp
version: "3"
services:
web:
build: .
volumes:
- .:/sinatra-inhouse-app
- ruby-bundle:/usr/local/bundle
working_dir: /sinatra-inhouse-app
ports:
- 4567:4567
command: bundle exec rackup --host 0.0.0.0 --port 4567
volumes:
ruby-bundle:
driver: local
起動してみる
$ docker-compose up
Creating sinatra-inhouse-app_web_1 ... done
Attaching to sinatra-inhouse-app_web_1
web_1 | [2020-10-07 14:19:22] INFO WEBrick 1.6.0
web_1 | [2020-10-07 14:19:22] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux-musl]
web_1 | [2020-10-07 14:19:22] INFO WEBrick::HTTPServer#start: pid=1 port=4567
GitHub認証に対応する
今はまだハローワールド。
それでも、社内サービスなので世の中に見られてはいけない。(とする。)
OAuthアプリケーション登録
https://github.com/settings/developers からアプリケーション登録をして、Client IDとSecretをもらってくる。
Authorization callback URL だけが重要で、あとはてきとうでいい。
.envrc にメモる
direnv が便利なので、それを使う。↑でメモったやつをそのまま書く。
export GITHUB_CLIENT_ID=eb12427ce9d12604f813
export GITHUB_CLIENT_SECRET=04d6af0463f16a807d7faca9ead406e76ad8192992c022ee
ちなみに、このファイルは絶対にGit管理してはいけない。
(もちろん、Qiitaに本物のIDを書いちゃダメなので、↑はサンプル。)
/.envrc
$ direnv allow .
あとは、docker-compose.ymlに environment
を追加すれば、コンテナ側で管渠変数が見れる。
diff --git a/docker-compose.yml b/docker-compose.yml
index b317508..2d75301 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,6 +6,9 @@ services:
- .:/sinatra-inhouse-app
- ruby-bundle:/usr/local/bundle
working_dir: /sinatra-inhouse-app
+ environment:
+ - GITHUB_CLIENT_ID
+ - GITHUB_CLIENT_SECRET
ports:
- 4567:4567
command: bundle exec rackup --host 0.0.0.0 --port 4567
$ docker-compose run --rm web sh
/sinatra-inhouse-app # echo $GITHUB_CLIENT_ID
eb12427ce9d12604f813
omniauth-githubを入れる
OmniAuthをSinatraに組み込む方法はここに書いてある。
https://github.com/omniauth/omniauth/wiki/Sinatra-Example
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "sinatra", require: false
gem "omniauth"
gem "omniauth-github"
diff --git a/inhouse_app.rb b/inhouse_app.rb
index b2621c7..1716520 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -1,7 +1,18 @@
require 'sinatra/base'
class InhouseApp < Sinatra::Base
+ use Rack::Session::Cookie
+ use OmniAuth::Builder do
+ provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET']
+ end
+
get '/' do
'<h1>It works!</h1>'
end
+
+ get '/auth/github/callback' do
+ auth_hash = request.env['omniauth.auth']
+ puts auth_hash
+ redirect to('/')
+ end
end
動作確認
こんなかんじで、あとはログを見ると
I, [2020-10-07T14:45:12.104513 #1] INFO -- omniauth: (github) Request phase initiated.
I, [2020-10-07T14:45:12.531651 #1] INFO -- omniauth: (github) Callback phase initiated.
#<OmniAuth::AuthHash credentials=#<OmniAuth::AuthHash expires=false token="???????????????????"> extra=#<OmniAuth::AuthHash all_emails=#<Hashie::Array []> raw_info=#<OmniAuth::AuthHash avatar_url="https://avatars2.githubusercontent.com/u/11763113?v=4" bio=nil blog="http://yusukeiwaki.hatenablog.com/" company=nil created_at="2015-04-02T00:02:12Z" email=nil events_url="https://api.github.com/users/YusukeIwaki/events{/privacy}" followers=9 followers_url="https://api.github.com/users/YusukeIwaki/followers" following=19 following_url="https://api.github.com/users/YusukeIwaki/following{/other_user}" gists_url="https://api.github.com/users/YusukeIwaki/gists{/gist_id}" gravatar_id="" hireable=nil html_url="https://github.com/YusukeIwaki" id=11763113 location="Fukuoka, Japan" login="YusukeIwaki" name="Yusuke Iwaki" node_id="MDQ6VXNlcjExNzYzMTEz" organizations_url="https://api.github.com/users/YusukeIwaki/orgs" public_gists=32 public_repos=147 received_events_url="https://api.github.com/users/YusukeIwaki/received_events" repos_url="https://api.github.com/users/YusukeIwaki/repos" site_admin=false starred_url="https://api.github.com/users/YusukeIwaki/starred{/owner}{/repo}" subscriptions_url="https://api.github.com/users/YusukeIwaki/subscriptions" twitter_username=nil type="User" updated_at="2020-10-07T14:42:15Z" url="https://api.github.com/users/YusukeIwaki"> scope=""> info=#<OmniAuth::AuthHash::InfoHash email=nil image="https://avatars2.githubusercontent.com/u/11763113?v=4" name="Yusuke Iwaki" nickname="YusukeIwaki" urls=#<OmniAuth::AuthHash Blog="http://yusukeiwaki.hatenablog.com/" GitHub="https://github.com/YusukeIwaki">> provider="github" uid="11763113">
こんなのがでているはず。
ハローワールドを認証つきにする
ここに詳しく書いてある。
http://sinatrarb.com/faq.html#auth
認証されていなかったらGitHubログインの方に飛ばし、
許可のないユーザであればForbidden表示をする。
diff --git a/inhouse_app.rb b/inhouse_app.rb
index 1716520..29726af 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -6,13 +6,38 @@ class InhouseApp < Sinatra::Base
provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET']
end
+ helpers do
+ def authorize!
+ unless authenticated?
+ redirect to('/auth/github')
+ end
+ unless authorized?
+ halt 403, 'Forbidden. <a href="/logout">Logout</a>'
+ end
+ end
+
+ def authenticated?
+ session[:user]
+ end
+
+ def authorized?
+ session[:user] == 'YusukeIwaki'
+ end
+ end
+
get '/' do
+ authorize!
'<h1>It works!</h1>'
end
get '/auth/github/callback' do
auth_hash = request.env['omniauth.auth']
- puts auth_hash
+ session[:user] = auth_hash['extra']['raw_info']['login']
+ redirect to('/')
+ end
+
+ get '/logout' do
+ session[:user] = nil
redirect to('/')
end
end
ためしに他のユーザでアクセスしてみると、こんなかんじ。
Herokuにデプロイする
認証が最低限かけれたところで、Herokuに上げてみる。
$ heroku create sinatra-inhouse-app-sample
Creating ⬢ sinatra-inhouse-app-sample... done
https://sinatra-inhouse-app-sample.herokuapp.com/ | https://git.heroku.com/sinatra-inhouse-app-sample.git
$ git push heroku master
Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 12 threads
Compressing objects: 100% (18/18), done.
Writing objects: 100% (21/21), 3.04 KiB | 3.04 MiB/s, done.
Total 21 (delta 7), reused 3 (delta 0), pack-reused 0
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Ruby app detected
remote: -----> Installing bundler 2.1.4
remote: -----> Removing BUNDLED WITH version in the Gemfile.lock
remote: -----> Compiling Ruby/Rack
remote: -----> Using Ruby version: ruby-2.6.6
remote: -----> Installing dependencies using bundler 2.1.4
remote: Running: BUNDLE_WITHOUT=development:test BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
remote: Fetching gem metadata from https://rubygems.org/.......
remote: Using bundler 2.1.4
remote: Fetching hashie 4.1.0
remote: Fetching multipart-post 2.1.1
remote: Fetching jwt 2.2.2
remote: Installing jwt 2.2.2
Herokuはとても賢いので、GemfileがあればRubyビルドを開始してくれる。
あとは、環境変数をセットして
$ heroku config:set GITHUB_CLIENT_ID=eb12427ce9d12604f813
$ heroku config:set GITHUB_CLIENT_SECRET=04d6af0463f16a807d7faca9ead406e76ad8192992c022ee
OAuthのアプリケーション設定でコールバックURLをHerokuのものに書き換えて、
これだけで、Herokuにアクセスしたら認証もしっかり動いて、いいかんじ。
おまけ: bin/consoleを使えるようにする
SinatraではRailsコンソールは使えないが、それに近いものは使えるようにしておく。
#!/usr/bin/env ruby
require "bundler/setup"
Bundler.require
require_relative '../inhouse_app'
Pry.start
diff --git a/Gemfile b/Gemfile
index 4fa2b7b..aef2284 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,6 +4,7 @@ source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+gem "pry"
gem "sinatra", require: false
gem "omniauth"
$ docker-compose run --rm web sh
/sinatra-inhouse-app # bundle install
Fetching gem metadata from https://rubygems.org/.......
Resolving dependencies...
Using bundler 2.1.2
Using coderay 1.1.3
Using multipart-post 2.1.1
Using faraday 1.0.1
Using hashie 4.1.0
Using jwt 2.2.2
Using method_source 1.0.0
Using multi_json 1.15.0
Using multi_xml 0.6.0
Using ruby2_keywords 0.0.2
Using mustermann 1.1.1
Using rack 2.2.3
Using oauth2 1.4.4
Using omniauth 1.9.1
Using omniauth-oauth2 1.7.0
Using omniauth-github 1.4.0
Fetching pry 0.13.1
Installing pry 0.13.1
Using rack-protection 2.1.0
Using tilt 2.0.10
Using sinatra 2.1.0
Bundle complete! 4 Gemfile dependencies, 20 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
/sinatra-inhouse-app # bin/console
[1] pry(main)> InhouseApp
=> InhouseApp
[2] pry(main)> InhouseApp.run
=> false
[3] pry(main)> exit
中間おさらい
ここまでで
- Docker, docker-composeで開発環境はサッと作れるのがよい。
- Herokuにデプロイしたい。
- GitHub認証で、なおかつ知らないユーザは弾きたい。
については比較的簡単に作れた。
ここまでだと「あれ?Sinatraって結構べんりじゃね?」と錯覚する。
そう、錯覚。
なぜなら、ここまではRailsで作ったとしても、ほとんどRailsのフレームワークの機能を使わないからだ。
フォーム入力したらそれに沿って外部サービスのAPIを叩きに行って、結果を表示するだけのアプリ。一応ActiveRecordは使用する。
ただし社内サービスなので、フォームバリデーションとかは適当でいい。あとは...
後半は、本体を作り始める。
ActiveRecordを使う
ここから徐々にSinatraのつらみがでてくる。
Railsであればconfig/database.ymlをいじって、app/models/ にクラスを追加すれば、DBアクセスができる状態になるが、Sinatraはsinatra-activerecordなどの設定が必要だ。
docker-composeにpostgresqlを追加する
ローカル環境でもpostgresqlを動かす。手順は「postgresql docker-compose」とかでググれば出てくるだろう。
diff --git a/docker-compose.yml b/docker-compose.yml
index 2d75301..26f4207 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,12 +7,28 @@ services:
- ruby-bundle:/usr/local/bundle
working_dir: /sinatra-inhouse-app
environment:
+ - POSTGRES_HOST=postgresql
+ - POSTGRES_USER=dbuser
+ - POSTGRES_PASSWORD=dbpassword
- GITHUB_CLIENT_ID
- GITHUB_CLIENT_SECRET
+ depends_on:
+ - postgresql
ports:
- 4567:4567
command: bundle exec rackup --host 0.0.0.0 --port 4567
+ postgresql:
+ image: postgres:12-alpine
+ restart: always
+ environment:
+ POSTGRES_USER: dbuser
+ POSTGRES_PASSWORD: dbpassword
+ volumes:
+ - postgresql-data:/var/lib/postgresql/data
+
volumes:
ruby-bundle:
driver: local
+ postgresql-data:
+ driver: local
$ docker-compose pull
これで、postgresqlがdocker-compose upやdocker-compose run で起動するようになる。
sinatra-activerecordを入れる
Gemfileに追記。
あと、postgresql関連のライブラリを入れるために(bundle installを通すために)Dockerfileで build-baseとpostgresql-devのインストールプロセスを追加する必要がある。
lessはPryの標準出力が長くなってもバグらないように追加。
diff --git a/Dockerfile b/Dockerfile
index 5ac8708..ab0beb4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1 +1,6 @@
FROM ruby:2.7-alpine
+
+RUN apk add --no-cache \
+ build-base \
+ less \
+ postgresql-dev
diff --git a/Gemfile b/Gemfile
index aef2284..19d0a62 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,8 +4,12 @@ source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+gem "activerecord"
+gem "pg"
gem "pry"
+gem "rake"
gem "sinatra", require: false
+gem "sinatra-activerecord"
gem "omniauth"
gem "omniauth-github"
rake db:create
sinatra-activerecordのREADMEにある説明から、雰囲気で設定ファイルなどを追加する。
config/database.yml については、Railsのものをそのまま使える。
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV.fetch("POSTGRES_HOST") { '127.0.0.1' } %>
port: 5432
username: <%= ENV["POSTGRES_USER"] %>
password: <%= ENV["POSTGRES_PASSWORD"] %>
development:
<<: *default
database: inhouse_app
test:
<<: *default
database: inhouse_app_test
production:
url: <%= ENV['DATABASE_URL'] %>
require "sinatra/activerecord/rake"
namespace :db do
task :load_config do
require "bundler/setup"
Bundler.require
require "./inhouse_app"
end
end
/sinatra-inhouse-app # bundle exec rake -T
rake db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_E...
rake db:create_migration # Create a migration (parameters: NAME, VERSION)
rake db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV...
rake db:environment:set # Set the environment value for the database
rake db:fixtures:load # Loads fixtures into the current environment's database
rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status # Display status of migrations
rake db:prepare # Runs setup if database does not exist, or runs migrations if it does
rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:cache:clear # Clears a db/schema_cache.yml file
rake db:schema:cache:dump # Creates a db/schema_cache.yml file
rake db:schema:dump # Creates a db/schema.rb file that is portable against any DB supported by Active Record
rake db:schema:load # Loads a schema.rb file into the database
rake db:seed # Loads the seed data from db/seeds.rb
rake db:seed:replant # Truncates tables of each database for current environment and loads the seeds
rake db:setup # Creates the database, loads the schema, and initializes with the seed data (use db:re...
rake db:structure:dump # Dumps the database structure to db/structure.sql
rake db:structure:load # Recreates the databases from the structure.sql file
rake db:version # Retrieves the current schema version number
/sinatra-inhouse-app # bundle exec rake db:create
Created database 'inhouse_app'
Created database 'inhouse_app_test'
rake db:migrate
Railsジェネレータがない。 rails g migration
できないので、代わりに rake db:create_migration
を使う。
/sinatra-inhouse-app # bundle exec rake db:create_migration NAME=create_audit_log
db/migrate/20201007162814_create_audit_log.rb
作ってしまえばRailsと同じなのだが、いざcreate_tableを手で書くと、割と迷う。
class CreateAuditLog < ActiveRecord::Migration[6.0]
def up
create_table :audit_logs do |t|
t.string :subject, null: false
t.text :description
t.datetime :created_at, index: true, null: false
end
end
def down
drop_table :audit_logs
end
end
db:migrateはRailsと同じ。
/sinatra-inhouse-app # bundle exec rake db:migrate
== 20201007162814 CreateAuditLog: migrating ===================================
-- create_table(:audit_logs)
-> 0.0090s
== 20201007162814 CreateAuditLog: migrated (0.0091s) ==========================
モデルの作成
class AuditLog < ActiveRecord::Base
validates :subject, presence: true
end
作っておしまい...ではない。Railsと違って、Sinatraはオートロードみたいな仕組みを持たないので、明示的にrequireしてあげないといけないのだ。
diff --git a/inhouse_app.rb b/inhouse_app.rb
index 5cd6ed5..a3ab821 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -1,4 +1,8 @@
require 'sinatra/base'
+Dir['./app/models/**/*.rb'].each { |f| require_relative f }
+
+ActiveRecord::Base.logger = Logger.new($stdout)
+ActiveRecord::Base.verbose_query_logs = true
class InhouseApp < Sinatra::Base
use Rack::Session::Cookie
ここまでくれば、あとは自由にActiveRecordが使える
/sinatra-inhouse-app # bin/console
[1] pry(main)> AuditLog.last
D, [2020-10-07T16:35:50.957634 #239] DEBUG -- : AuditLog Load (0.7ms) SELECT "audit_logs".* FROM "audit_logs" ORDER BY "audit_logs"."id" DESC LIMIT $1 [["LIMIT", 1]]
D, [2020-10-07T16:35:50.957900 #239] DEBUG -- : ↳ (pry):1:in `__pry__'
=> nil
[2] pry(main)> AuditLog.count
D, [2020-10-07T16:35:54.177493 #239] DEBUG -- : (0.9ms) SELECT COUNT(*) FROM "audit_logs"
D, [2020-10-07T16:35:54.177788 #239] DEBUG -- : ↳ (pry):2:in `__pry__'
=> 0
アクセスカウンターを作ってHerokuにデプロイする
DBとの結合動作確認のためだけに、とりあえずアクセスカウンタを付けてみる。
diff --git a/inhouse_app.rb b/inhouse_app.rb
index a3ab821..498ec31 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -31,7 +31,13 @@ class InhouseApp < Sinatra::Base
get '/' do
authorize!
- '<h1>It works!</h1>'
+
+ AuditLog.create!(
+ subject: "#{session[:user]} accessed",
+ description: "#{session[:user]} accessed on '/'",
+ )
+
+ "<h1>It works!</h1> -- #{AuditLog.count}"
end
get '/auth/github/callback' do
リロードするたびにカウンタが増えることを確認したら、
$ git push heroku master
で、デプロイ。データベースは勝手に追加されたりはしないので、手動で追加。
$ heroku addons:create heroku-postgresql:hobby-dev
Creating heroku-postgresql:hobby-dev on ⬢ sinatra-inhouse-app-sample... free
Database has been created and is available
! This database is empty. If upgrading, you can transfer
! data from another database with pg:copy
Created postgresql-curved-40316 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation
最初はDBがマイグレーションされていないので、エラー画面になるが、驚く必要はない。
$ heroku run bundle exec rake db:migrate
上記のように、heroku runでマイグレーションを実行すれば直る。
フォームを作る
actionview
まで入れてしまうと、bundle install でNokogiriが入ってくるので、「それはもはやRailsでは?」みたいな状況になる。
素のSinatraでもerbは使えるので、erbで頑張ってみる。
これが、フォームヘルパーもタグヘルパーも使えないので、割と苦行だ。
index.html.erbを読み込むには?
ERBを使うには erb
っていうメソッドを使うわけだが、READMEのように erb :index
と指定するとviews/index.html.erbではなく、views/index.erbを探しに行こうとする。
かといって、 erb "index.html"
と指定すると、index.htmlという文字列をERBとして解釈しようとする。
なので、無理矢理でも :'index.html'
のようなシンボルを指定する必要がある。
diff --git a/inhouse_app.rb b/inhouse_app.rb
index 498ec31..882dc60 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -37,7 +37,7 @@ class InhouseApp < Sinatra::Base
description: "#{session[:user]} accessed on '/'",
)
- "<h1>It works!</h1> -- #{AuditLog.count}"
+ erb :'index.html'
end
get '/auth/github/callback' do
<html>
<body>
<h1>It works!</h1> -- <%= AuditLog.count %>
from erb
</body>
</html>
フォームを作る
フォームオブジェクトはRailsと同様に書けるのだけど、form_for, form_withが無いので、愚直に <form action="..." method="POST">
から書かないといけない。
フォームのHTMLを手で書きたい人にはいいかもしれないが、そこまでフォームの見た目は気にしない社内サービスだと、これは結構めんどくさい。
diff --git a/inhouse_app.rb b/inhouse_app.rb
index 882dc60..75ecdf1 100644
--- a/inhouse_app.rb
+++ b/inhouse_app.rb
@@ -40,6 +40,22 @@ class InhouseApp < Sinatra::Base
erb :'index.html'
end
+ get '/profile' do
+ @form = ProfileForm.new
+ erb :'profile.html'
+ end
+
+ post '/profile' do
+ profile_form_params = params.slice(:name, :age, :address)
+ @form = ProfileForm.new(profile_form_params)
+ if @form.valid?
+ "Profile登録しにいく"
+ redirect to('/profile')
+ else
+ erb :'profile.html'
+ end
+ end
+
get '/auth/github/callback' do
auth_hash = request.env['omniauth.auth']
session[:user] = auth_hash['extra']['raw_info']['login']
class ProfileForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :name, :string
attribute :age, :integer
attribute :address, :string
validates :name, presence: true
validates :age, presence: true
end
<html>
<body>
<h1>Profile</h1>
<% if @form.errors.present? %>
<ul class="errors">
<% @form.errors.full_messages.each do |error_message| %>
<li><%= error_message %></li>
<% end %>
</ul>
<% end %>
<form action='/profile' method='POST'>
<div>
name: <input type="text" name="name" value="<%= @form.name %>" />
</div>
<div>
age: <input type="number" name="age" value="<%= @form.age %>" />
</div>
<div>
address: <input type="text" name="address" value="<%= @form.address %>" />
</div>
<input type="submit" value="OK" />
</form>
</body>
</html>
GET /profile | POST /profile バリデーションエラー |
---|---|
まとめ
ActiveRecordを使う・画面(とくにフォーム)がある・HerokuにデプロイするようなWebアプリケーション書くなら、たとえ単機能であってもSinatraではなくRailsを選択したほうが、フレームワークの恩恵を受けやすいです。