- 投稿日:2020-07-02T23:45:33+09:00
なんとActiveRecordはDB跨ぎでの関連付けが可能っぽい
業務中に知って驚いたこと。休日に気が向いたら深堀するかも。
前提
User has_many Posts through UserPosts
という関連付けをしたい。User
テーブルは、Posts
テーブルとUserPosts
テーブルとは異なるDBのテーブル。予想
User.rbhas_many :posts has_many :read_posts, source: :posts, through: :user_postsなんてasociationをモデル層に書いても流石に
User.read_posts
は取ってこられないだろうと鼻を括っていた。しかしだ。。。
- ActiveRecordがpreloadを使っているのなら、関連付け可能で取ってこれる
- ActiveRecordがeagerloadを使っているのなら、関連付け不可能(JOINを使っているから)
詳しいことは時間があったら調べてくれ
- 投稿日:2020-07-02T22:32:22+09:00
Ruby on Rails チュートリアル(第4版) 第3章
3.1 演習
1.BitbucketがMarkdown記法のREADME (リスト 3.3) をHTMLとして正しく描画しているか、確認してみてください。
省略
2.本番環境 (Heroku) のルートURLにアクセスして、デプロイが成功したかどうか確かめてみてください。
省略3.2.1 演習
1.Fooというコントローラを生成し、その中にbarとbazアクションを追加してみてください。
$ rails generate controller Foo bar baz2.コラム 3.1で紹介したテクニックを駆使して、Fooコントローラとそれに関連するアクションを削除してみてください。
$ rails destroy controller Foo bar baz3.4.2 演習
1.StaticPagesコントローラのテスト (リスト 3.24) には、いくつか繰り返しがあったことにお気づきでしょうか? 特に「Ruby on Rails Tutorial Sample App」という基本タイトルは、各テストで毎回同じ内容を書いてしまっています。そこで、setupという特別なメソッド (各テストが実行される直前で実行されるメソッド) を使って、この問題を解決したいと思います。まずは、リスト 3.30のテストが green になることを確認してみてください (リスト 3.30では、2.2.2で少し触れたインスタンス変数や文字列の式展開というテクニックを使っています。それぞれ4.4.5と4.2.2で詳しく解説するので、今はわからなくても問題ありません)。
GREENになった3.4.3 演習
1.サンプルアプリケーションにContact (問い合わせ先) ページを作成してください16 (ヒント: まずはリスト 3.15を参考にして、/static_pages/contactというURLのページに「Contact | Ruby on Rails Tutorial Sample App」というタイトルが存在するかどうかを確認するテストを最初に作成しましょう。次に、3.3.3でAboutページを作ったときのと同じように、Contactページにもリスト 3.40のコンテンツを表示してみましょう。)。
/test/controllers/static_pages_controller_test.rbtest "should get contact" do get static_pages_contact_url assert_response :success assert_select "title", "Contact | #{@base_title}" end/config/routes.rbget 'static_pages/contact'/app/controllers/static_pages_controller.rbdef contact end/app/views/static_pages/contact.html.erb<% provide(:title, "Contact") %> <h1>Contact</h1> <p> Contact the Ruby on Rails Tutorial about the sample app at the <a href="https://railstutorial.jp/contact">contact page</a>. </p>3.4.4 演習
1.リスト 3.41にrootルーティングを追加したことで、root_urlというRailsヘルパーが使えるようになりました (以前、static_pages_home_urlが使えるようになったときと同じです)。リスト 3.42のFILL_INと記された部分を置き換えて、rootルーティングのテストを書いてみてください。
/test/controllers/static_pages_controller_test.rbtest "should get root" do get root_url assert_response :success end2.実はリスト 3.41のコードを書いていたので、先ほどの課題のテストは既に green になっているはずです。このような場合、テストを変更する前から成功していたのか、変更した後に成功するようになったのか、判断が難しいです。リスト 3.41のコードがテスト結果に影響を与えていることを確認するため、リスト 3.43のようにrootルーティングをコメントアウトして見て、 red になるかどうか確かめてみましょう (なおRubyのコメント機能については4.2.1で説明します)。最後に、コメントアウトした箇所を元に戻し (すなわちリスト 3.41に戻し)、テストが green になることを確認してみましょう。
省略メモ
assert_select
メソッドでは、特定のHTMLタグが存在するかどうかをテストする (この種のアサーションメソッドはその名から「セレクタ」と呼ばれることがある)provide
メソッドを使ってタイトルをページごとに変更する。<%= yield %>
は、各ページの内容をレイアウトに挿入するためのもの。- コントローラを新規作成するためのrailsコマンドは
rails generate controller ControllerName アクション名 (省略可)
。
- 投稿日:2020-07-02T18:46:43+09:00
【Ruby理解度チェック】何が起きているか説明できますか? &&とandの違い
面白い質問があったのでテスト形式でシェア
現象
def ampersand_return puts "foo" && return puts "bar" end def and_return puts "foo" and return puts "bar" end > ampersand_return => nil > and_return foo bar => nil何が起きているか説明できますか?
回答
原則として、
&&
とand
の違いはその優先順位にある
&& return
の方
&&
が先に演算され、以下と同値になるputs ("foo" && return)
"foo"
はtruthyなので、右辺のreturn
が処理され、メソッドは何も出力せずreturnする。
and return
の方
puts "foo"
が先に演算され、以下と同値になる(puts "foo") and return
puts
はnil
を返すのでand
の右辺は処理されず、"foo"
と"bar"
両方が出力されメソッドは正常終了する。終わりに
and
の例のようにmethod callが優先される演算子に関しては、ドキュメントには明記されていない?よう
このStack Overflow Answerによるとand
,or
,if
,unless
,until
,while
,rescue
がこの挙動をするらしい
- 投稿日:2020-07-02T18:46:43+09:00
【Ruby理解度チェック】何が起きているか説明できますか? &&とand
面白い質問があったのでテスト形式でシェア
現象
def ampersand_return puts "foo" && return puts "bar" end def and_return puts "foo" and return puts "bar" end > ampersand_return => nil > and_return foo bar => nil何が起きているか説明できますか?
回答
原則として、
&&
とand
の違いはその優先順位にある
&& return
の方
&&
が先に演算され、以下と同値になるputs ("foo" && return)
"foo"
はtruthyなので、右辺のreturn
が処理され、メソッドは何も出力せずreturnする。
and return
の方
puts "foo"
が先に演算され、以下と同値になる(puts "foo") and return
puts
はnil
を返すのでand
の右辺は処理されず、"foo"
と"bar"
両方が出力されメソッドは正常終了する。終わりに
and
の例のようにmethod callが優先される演算子に関しては、ドキュメントには明記されていない?よう
このStack Overflow Answerによるとand
,or
,if
,unless
,until
,while
,rescue
がこの挙動をするらしい
- 投稿日:2020-07-02T18:39:30+09:00
【Rails】hamlコード早見表
Github公式 Haml-rails
チュートリアル HamlTutorial導入
Gemfile# 以下を追加し`bundle install` gem "haml-rails", "~> 2.0" # Rails4の場合 gem "haml-rails", "~> 1.0.0" # Rails3の場合 gem "haml-rails", "~> 0.4.0"html.erbをhtml.hamlに変換
$ rails generate haml:application_layout convert
全てのerbファイルを変換する場合
$ rails haml:erb2haml
HTMLコード
erb<div>blue</div> <div class="sky">blue</div> <div class="sky">blue</div> <div class="sky" id="line">blue</div>haml%div blue %div.sky blue .sky blue .sky#line blueRubyコード
erb<% if user_signed_in? %> <%= hoge %> <% else %> <%= skyblue %> <% end %>haml- if user_signed_in? = hoge - else = skyblueネスト
erb<div class="header-nav"> <nav> <ul class="nav-list"> <% if current_user %> <li class="list-item"><%= content %></li> <li class="list-item" id="sky"><%= link_to "blue" %></li> <% else %> <li class="list-item"><%= content %></li> <li class="list-item" id="sky"><%= link_to "blue" %></li> <% end %> </ul> </nav> </div>haml.header-nav %nav %ul.nav-list - if current_user %li.list-item= content %li.list-item#sky= link_to "blue" - else %li.list-item= content %li.list-item#sky= link_to "blue"
- 投稿日:2020-07-02T17:44:15+09:00
Railsでproduction環境運用する際にしたほうがいい設定
ブラウザ上のエラー表示をdevelopmentとproduction環境で同じにする
まずdevelopment環境でERRORが発生するとブラウザに詳細情報が表示されます。
しかし、production環境だと何かしらのERRORが発生したとしても「We’re sorry, but something went wrong.」と表示されてしまいます。
そこでproduction環境でもブラウザに詳細情報を表示させるには
config.consider_all_requests_local = false
をtrue
にしてあげれば良い。config/environments/production.rbconfig.consider_all_requests_local = true参考文献
本番環境でDBをresetする
Rails5以降のproduction環境では、db:dropやdb:resetなどのDBを破壊する系のコマンド実行を防止する機能が追加された。
そのためproduction環境でbundle exec rake db:migrate:reset RAILS_ENV=production
などをしてもエラーを発生してしまう。対処法
環境変数に
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
を指定してあげれば良い。$ bundle exec rake db:migrate:reset RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1参考文献
Rails5のproductionでrake db:dropはできない、普通には
知っておくべきRailsのconfig/enviroments配下の設定
下記サイト一通り目を通すことでproduction環境で詰まった際に解決の糸口が分かるかも?
Railsのconfig/enviroments配下を読んでみる
- 投稿日:2020-07-02T16:59:14+09:00
【Rails】APIモードで開発を始めたけど、やっぱりcookie使いたい
はじめに
自分用なので手順しか書かない
手順
controllerに追記
controllerでcookiesを呼び出す時の話
APIモードだとcookies
がそもそも呼び出せないApplicationController か、cookiesを呼びだすcontrollerに以下を追加
include ActionController::Cookiesmiddleware追加
middlewareを追加しないとcookieの読み出しも書き込みも実際してくれない
application.rbに以下の2行を追加
config.middleware.use ActionDispatch::Cookies config.middleware.use ActionDispatch::Session::CookieStoreおわり
ここまでやるとDeviseも自然にset-cookie返してくれるようになります
基本はDevise + jwtでやってたんですけど、API経由ではないルート追加することになり、header渡せず無事死亡しました
- 投稿日:2020-07-02T02:29:52+09:00
GETとPOST(GET以外)で同じコード、でも挙動が違う
ちゃんと調査しないと気付かなかったのでメモがてら、誰かが同じ問題に直面したときの助けになれば。
概要
ある日、GETとPOSTで挙動が違うという話が。
コードを見てみたら、コントローラーの同じアクションにGETとPOSTがルーティングされていました。
どう見ても同じ処理。そこに差異は無いので挙動ベースでデバッグしてみることに。
テストコードでは上手く動いていました。でも直接APIを叩いてみると、たしかに違う挙動。最終的に気付いたのが、問題のあるリクエストではCSRFトークンが渡ってきていないことでした。
アクション内ではsessionが使われており、CSRFトークンが渡されていないことでリクエストは空のsessionに対して処理されていました。これは元々のsessionとは別のものとして扱われ、その結果挙動が変わってしまっていました。詳細
例としてこのようなコントローラがあるとします。
(POSTの代わりにDELETEにしていますが、挙動は同様です)app/controllers/session_controller.rbclass SessionsController < ApplicationController def destroy session.delete(:user_id) end endそしてこのようにルーティングされています。
config/routes.rbmatch 'logout', to: 'sessions#destroy', via: [:get, :delete]前提として
session[:user_id]
には既に値が格納されているとします。
このとき、以下の2つはどのように動作するでしょうか?
GET /sessions/destroy
へのAPIリクエストDELETE /sessions/destroy
へのAPIリクエストコードだけ見ると、明らかにどちらも
session[:user_id]
が削除されそうです。疑いようがありません。ただし、 これはCSRFトークンがリクエストに追加されているか否かで挙動が変わります。2つともCSRFトークンが追加されていない場合、答えはこうです。
GET /sessions/destroy
へのAPIリクエストはsession[:user_id]
が削除されるDELETE /sessions/destroy
へのAPIリクエストsession[:user_id]
が削除されないなぜか?
ここでApplicationController
を覗いてみましょう。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base protect_from_forgery with: :null_session endおっと、なにやら
protect_from_forgery
というコードが。
※ApplicationControllerに書かれているかは各プロダクトごとに異なりますこの
protect_from_forgery with: :null_session
が関係しています。
ドキュメントには以下の記述があります。CSRF protection is turned on with the protect_from_forgery method. By default protect_from_forgery protects your session with :null_session method, which provides an empty session during request.
CSRF保護は、protect_from_forgeryメソッドでオンになります。デフォルトでは、protect_from_forgeryは、リクエスト中に空のセッションを提供する:null_sessionメソッドでセッションを保護します。
(Google翻訳)この仕様によって、CSRFトークンによる検証が正しくない場合に
session
の中身が空の状態で、かつ別物として処理されます。また、GETではそもそもCSRFトークンの検証がされないため、session
の中身は本来の状態で動作します。
その結果、消したはずのsession[:user_id]
が消えていないという挙動になります。振り返り
なぜちゃんと調査しなければ気付かなかったか?
上記の例ではもしかすると気づけたかもしれません。ですが、
protect_from_forgery
はApplicationController
側に書かれていたり、それによって普段はあまり意識しなかったりします。これが自分で書いたコードでなければ尚更です。一度経験すればおそらく以後は気付けると思うので、良い経験になりました。そもそもなぜGETとPOST(GET以外)を同じアクションにルーティングしているのか?
これについては経緯が不明でした。(実際のコードにはコメントがありお気持ちだけは受け止めました)
Railsガイドにもこのような記載があります。1つのアクションにGETリクエストとPOSTリクエストを両方ルーティングすると、セキュリティに影響する可能性があります。本当に必要な理由がない限り、1つのアクションにすべてのHTTP動詞をルーティングすることは避けてください。
GETとPOSTはRFCで定義された仕様としても別物なので、同じアクションにルーティングするのは避けるべきかなと思います。
さいごに
おそらく今回のものはかなりレアケースかなぁと思います。とはいえどこかの誰かがハマってしまうと悲しいのと、この記事に目を触れた誰かが同様のルーティングをしない、またはルーティングを考える時に注意できる種になればと思います。
お気づきの点があれば気軽にコメントお願いします。些細なことでも歓迎です
- 投稿日:2020-07-02T00:14:34+09:00
Rails テーブル作成、カラムの追加や削除
はじめに
よく忘れるので、メモ
モデル作成 テーブル作成
railsでモデルを作成する際に、テーブルを作成するためのマイグレーションファイルが同時に作られます。
↓モデル作成コマンド$ rails generate model [モデル名] [属性名:データ型 属性名:データ型・・・] [オプション] 省略版 $ rails g model [モデル名] [属性名:データ型 属性名:データ型・・・] [オプション]-コマンドを実行すると、自動で作られるファイル
①モデルのクラスファイル
②マイグレーションファイル
③モデルの自動テスト
④モデルの自動テストで使うfictureファイル例 $ rails g model User name:string email:string 上のコマンドで作ると下のマイグレーションができる。 class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end endそしたら、マイグレーションをする。
$ rails db:migrate終わり
テーブル削除 カラム追加・削除
※クラス名はAddXXXToYYY、またはRemoveXXXFromYYYとします。
「XXX」にはテーブル名、YYYにはカラム名(複数のカラムを変更したい場合などがあるのでカラム名ではなくても大丈夫)カラム追加のマイグレーション作成
※memo(text型)のカラムを追加する場合
例 $ rails g migration AddMemoToUsers memo:text作成されたら$ rails db:migrateを実行し反映させます。
カラム削除のマイグレーション作成
※name(string型)のカラムを削除したい場合
例 $ rails g migration RemoveNameFromUsers name:string作成されたら$ rails db:migrateを実行し反映させます。
テーブルの削除
マイグレーションファイルの作成
本当はusersとしたかったところをuserssとしてしまい。テーブルを削除することに。なので、マイグレーションファイルを作成する。例 $ rails g migration userssマイグレーションファイル編集
消したいテーブルを指定して、drop_table :userssと記述する。例 class DeleteUserss < ActiveRecord::Migration[6.0] def change drop_table :userss end end下記のコマンドを叩くと、データベースから削除される。
$ rails db:migrate
ファイルの削除
ファイルツリーから直接消さずに、コマンドを実行してファイルを削除しましょう。
$ rails destroy migration クラス名 省略 $ rails d migration クラス名例 $rails d migration AddMemoToUsers