トップ 差分 一覧 Farm ソース 検索 ヘルプ PDF RSS ログイン

Diary/2022-10-2

Ruby on Rails はじめました

Getting Started with Railsを.ホストOSはUbuntu 20.04.

準備

RubyとSQLite3が必要,ということで,

$ sudo apt install ruby-full
$ sudo apt install sqlite3-doc
$ sudo apt install ruby-bundler

でずばっと.bundleはあれこれ必要になるのでいれとく.

$ ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-gnu]
$ sqlite3 --version
3.31.1 2020-01-27 19:55:54 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837balt1

が入った.で,gemで rails をインストール.

$ sudo gem install

やってみる

ガイド通りに

rails new blog

blog/ 以下にあれこれできている.

cd blog
./bin/rails server

でなんか起動した.

Unicorn/Nginx経由でアクセスできるようにする

【Rails】Webサーバー「Unicorn」の基本情報と実装方法を参考に
Gemfileに

gem "unicorm"

を追加.PIDとソケット,ログの格納用ディレクトリを作る.

mkdir -p tmp/pids tmp/sockets log

で,設定ファイルconfig/unicorn.rbを丸っと作成.

# Railsアプリのルート
rails_root = File.expand_path('../../', __FILE__)

# Gemfileの場所
ENV['BUNDLE_GEMFILE'] = rails_root + "/Gemfile"

# Unicornの設定
worker_processes  2
timeout           15
working_directory rails_root
pid               File.expand_path 'tmp/pids/unicorn.pid', rails_root
listen            File.expand_path 'tmp/sockets/.unicorn.sock', rails_root
stdout_path       File.expand_path 'log/unicorn.log', rails_root
stderr_path       File.expand_path 'log/unicorn.log', rails_root
preload_app       true

# フォークが行われる前の処理
before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      Process.kill "QUIT", File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end

# フォークが行われた後の処理
after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

# フォークが行われる前の処理
before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
end

# フォークが行われた後の処理
after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

# フォークが行われる前の処理
before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      Process.kill "QUIT", File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end

これで,

$ bundle exec unicorn -c config/unicorn.rb

とすると tmp/pids/unicorn.pid が作成されてPIDが格納されて,終了時に削除される.

Nginx側にエントリを追加するために site-available/hoge を編集.
Nginxからunicornにアクセスできるようにアップストリームの定義を追加して,

upstream unicorn {
        server unix://home/miyo/blog/tmp/sockets/.unicorn.sock;
}

/ へのアクセスを定義したアップストリームに振れるように

   location / {
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;
       proxy_pass http://unicorn;
   }

とか.で,

sudo nginx -t
sudo systemctl restart nginx.service 

で再起動.あとは,

bundle exec unicorn -c config/unicorn.rb

でUnicorn起動した後でブラウザから,アクセスする.
うまくNginx→unicornの設定ができていれば,
Blocked host: ホスト名って感じのエラーがでる.
調べてみると,DNSリバインディング攻撃を防ぐため,らしい.

config.hosts << "ホスト名"

を追加せよ,とあるので,config/environments/development.rb の

Rails.application.configure do
 ...
end

の末尾に,

config.hosts << "ホスト名"

を追加.あらためて

bundle exec unicorn -c config/unicorn.rb

で起動してブラウザからアクセスすると,なんかそれっぽいページが表示される.

Say Hello

MVCのVとCを作ってHello Worldしよう,という内容.
config/routes.rbを変更

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root "articles#index"
  get "/articles", to: "articles#index"

end

で,

bin/rails generate controller Articles index --skip-routes

としてcontrollerとviewを生成.

  • app/controllers/articles_controller.rb
  • app/views/articles/index.html.erb
  • test/controllers/articles_controller_test.rb
  • app/helpers/articles_helper.rb

が生成された.
app/views/articles/index.html.erb の 中身をガイドに従って編集して,
https://どこか/articles/ にアクセスしたら,index.html.erb の表示がみえた.
config/routes.rbをもう一度変更して,

Rails.application.routes.draw do
  root "articles#index"
  get "/articles", to: "articles#index"
end

としたら,https://どこか/ にアクセスして作成したindex.html.erb の表示がみえた.

Modelも作る

ガイド通りコマンドを実行してみる.

bin/rails generate model Article title:string body:text

ファイルが生成された.

  • db/migrate/20221002090126_create_articles.rb
  • app/models/article.rb
  • test/models/article_test.rb
  • test/fixtures/articles.yml

Articleっていう名前で,string型のtitleとbodyをもったモデルを作る,ということのよう.
db/migrate/20221002090126_create_articles.rbをみるとDBを作るcreate_tableが定義されている

bin/rails db:migrate

を実行.

bin/rails console

でirb使ってDBをいじる...ようだ.とりあえず言われるがままにやってみる.

article = Article.new(title: "Hello Rails", body: "I am on Rails!")
article.save

Article.find(1)とかArticle.allとかするとエントリをひける.
裏では,

SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]

とかってSQLが走っているようだ.
CとVを変更してブラウザからのアクセスで利用できるようにする.
app/controllers/articles_controller.rb を

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end
end

と編集し,app/views/articles/index.html.erb を

<h1>Articles</h1>
<ul>
  <% @articles.each do |article| %>
    <li>
      <%= article.title %>
    </li>
  <% end %>
</ul>

にする.erbの @articlesは,controller.rbの @articlesに対応している.
Rubyの return value が欲しくないときは <% %> を,欲しいときには <%= %>を使うらしい.

CRUDのReadを試す

routeを追加する.config/routes.rb をアップデート

Rails.application.routes.draw do
  root "articles#index"
  get "/articles", to: "articles#index"
  get "/articles/:id", to: "articles#show"
end

:id はrouteのパラメタ.
で,app/controllers/articls_controller.rbにコントローラを追加.
routeに追加した show と :id がどう使われるか見てとれる.

 def show
   @article = Article.find(params[:id])
 end

また,app/views/articles/show.html.erbを作成

<h1><%= @article.title %></h1>
<p><%= @article.body %></p>

Webブラウザで / にアクセスするとテーブルに登録された一覧とshowへのリンクが.
で,リンク先の /articles/1 などにアクセスすると,showの定義通りtitleとbodyが.

CRUD関連の便利な記法

CRUD関連のrouteをひとつひとつ定義するかわりに resources が使えるらしい.
config/routes.rb をアップデート

Rails.application.routes.draw do
  root "articles#index"
  resources :articles
end

bin/rails routesで確認すると,articlesに関連して,あれこれ定義されているのがわかる.

root         GET    /                            articles#index
articles     GET    /articles(.:format)          articles#index
             POST   /articles(.:format)          articles#create
new_article  GET    /articles/new(.:format)      articles#new
edit_article GET    /articles/:id/edit(.:format) articles#edit
article      GET    /articles/:id(.:format)      articles#show
             PATCH  /articles/:id(.:format)      articles#update
             PUT    /articles/:id(.:format)      articles#update
             DELETE /articles/:id(.:format)      articles#destroy

index.html.erbもaタグを自分で書くんじゃなくて,簡略化できる

<h1>Articles</h1>
<ul>
  <% @articles.each do |article| %>
    <li>
        <%= link_to article.title, article %>
    </li>
  <% end %>
</ul>

CRUDのCreate

サンプルまま作業.ただ,そのままだと,

ActionController::InvalidAuthenticityToken (HTTP Origin header (https://****) didn't match request.base_url (http://****)):

なエラーがでてPOSTがはじかれていた.
これはRails側ではなくて,Nginxでhttpsをほどているのが問題.
というわけで,site-available/hoge の location / {...} に,

proxy_set_header origin 'http://****';

を追加.

hoge.html.erbで,他の.html.erbファイルを読みこむことができる.

<%= render "form", article: @article %>

とかすると,_form.html.erbと,先頭に'_'が付いたファイルが読み込まれる,ようだ.