20200407のRubyに関する記事は22件です。

Rails コマンドが急に使えなくなった

久しぶりにrailsを起動しようとしたらrails sが通りませでした。
同じくrails -vやrails newをしても同じ様な状態になります。

色々調べたのですが、どうしても解決できずにこちらで質問させていただきます、、、
(初心者です、質問の仕方が間違っていたらすみません)

dlopen(/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin18/digest/md5.bundle, 9): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (LoadError)
Referenced from: /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin18/digest/md5.bundle
Reason: image not found - /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin18/digest/md5.bundle

rails sを打つと色々出てきますが、最後にこの様な文があります、、

rails newやrails -vを打っても同じ様な症状です。
自分ではどうすることもできず
初歩的な質問かもしれませんが、どなたかお助けいただけませんでしょうか??、
よろしくお願いします。

気になる事といえば
最近仮想開発環境を構築するのにVirtualBoxやvagrantを設定しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

開発環境、テスト環境、本番環境って何?雑にメモ

3つの環境が用意されているよ

例えば、herokuにpushする場合は、本番環境を使用しているみたい

こいつらは、RAILS_ENVという環境変数を用いて動作モードを切り替えられる

例えば、コマンドプロンプトで普通に指令を送る場合は、開発環境に送っている。

じゃあ「本番環境」を指定して実行したい時は、、、

このコードを記述すればおk

RAILS_ENV=production

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kinx ライブラリ - Integer

Integer

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

リポジトリ(https://github.com/Kray-G/kinx) のほうの説明を "Looks like JavaScript, feels like Ruby, and it is the script language fitting in C programmers." に変えてみた。意味的には例の名探偵のオマージュですが、英語の表現は違っていてオリジナルです。

今回は Integer です。

Integer オブジェクトに括り付いたメソッドは特殊メソッドで、整数値に直接作用させることができる。特殊メソッド、および特殊オブジェクトに関する詳細は Kinx ライブラリ - String を参照してください。

Integer 特殊オブジェクト

Integer オブジェクトに対して関数定義する例は以下の通り。

Integer.times100 = function(value) {
    return value * 100;
};
var val = 100.times100();
System.println(val);

実行してみよう。

10000

レシーバーが第 1 引数に来ます。

Integer

組み込み特殊メソッド

メソッド 意味
Integer.times(val, callback) i = 0 ~ (val - 1) の範囲として、callback があれば callback(i) の結果で、無ければ i で配列を作成して返す。
Integer.upto(val, max, callback) i = val ~ max の範囲で引数として callback(i) を呼ぶ。
Integer.downto(val, min, callback) i = min ~ val の範囲で引数として callback(i) を呼ぶ。
Integer.toString(val, base) val を文字列に変換する。base は 10 と 16 のみサポート。
Integer.toDouble(val) val を Double に変換する。

Math オブジェクト・メソッド

Integer オブジェクトには Math オブジェクトと同じ特殊メソッドが存在する。詳細は以下を参照。

具体例で書くと、例えば以下のように書ける。

var a = 2.pow(10);     // Math.pow(2, 10) と同じ  => 1024
var b = (-10).abs();   // Math.abs(-10)   と同じ  => 10

単項マイナス(-)は関数呼び出しより優先順位が低いため、カッコで括る必要があることに注意。

特殊オペレーター

単項 * オペレーター

単項 * オペレーターを整数値に適用した場合、文字コードに対応した文字列(1 文字)を返す。

var a = *97;  // => "a"

ちなみに逆変換(*a)は元に戻らない。文字列に対する単項 * オペレーターは配列になるため、上記例で *a とすると [97] と配列となることに注意。単独で文字コードを得るには、a[0] とする。

おわりに

Integer に特殊メソッドがあることでグッと Ruby っぽい感じがしてますが気のせいですかね。2.pow(10) とか書けるのが結構感慨深いなー。

ここ 見ながら、今サポートしてないメソッドとかサポートしてみようかなー、と。

ではまた次回。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列とハッシュに関して

対象読者

プログラミング初学者

配列に関して

配列(array)は複数の値をまとめて管理したいときに使用する。
配列内で管理されるものは”要素(element)”と呼ばれる。

配列の定義のしかた

#例1
array = Array.new

#例2
fruits = ["apple", "banana"]

配列に要素を追加する方法

型は以下の通り。

配列.push(追加する要素)

例えば

#空の配列を用意して
array = Array.new

#"A"を追加するには
array.push("A") 
puts array # 出力結果 ["A"]

また、一度に複数の要素を追加したい時は

#先ほどの配列をそのまま適用すると
array.push("B", "C")  
puts array # 出力結果 ["A", "B", "C"]

要素の間に要素を追加(挿入)する方法

型は以下の通り。

配列.insert(挿入位置, 挿入する要素)

例えば

#以下の配列があったとして
array = ["A", "B", "D"]

#"C"を挿入する場合は
array.insert(2, "C")
puts array # 出力結果 ["A", "B", "C", "D"]

また、一度に複数の要素を挿入したい時は、第3引数以降に要素を渡す。

#先ほどの配列をそのまま適用すると
array.insert(2, 1, 2) 
puts array # 出力結果 ["A", "B", 1, 2, "C", "D"] 

配列内の要素を削除する方法

型は以下の通り。

配列.delete(削除する要素)

例えば

#以下の配列があったとして
array = ["A", "B", 1, 1, 2, "C", "D"] 

#1を削除するには
array.delete(1) 
puts array # 出力結果 ["A", "B", 2, "C", "D"]

()内の引数に渡した要素と一致するものをすべて配列から削除する。

指定位置の要素を削除する方法

型は以下の通り。

配列.delete_at(削除位置)

例えば

#以下の配列があったとして
array = ["A", "B", 2, "C", "D"]

#2を削除するには
array.delete_at(2)
puts array # 出力結果 ["A", "B", "C", "D"]

指定範囲の要素を削除する方法

型は以下の通り。

配列.slice!(削除開始位置, 削除する長さ)

例えば

#以下の配列があったとして
array = ["A", "B", 1, 1, 2, "C", "D"] 

#1,1,2を削除するには
array.slice!(2, 3) # 出力結果 ["A", "B", "C", "D"]

#または
array.slice!(2..4)

#上記の型は
配列.slice!(削除範囲)

配列の中の要素を取り出す方法

型は以下の通り。

array[要素番号]

例えば

#以下の配列があったとして
array = ["A", "B", "C", "D"]

# "A"を取得するには
puts array[0] # 出力結果 "A" 

ハッシュに関して

キーとバリュー(値)を組み合わせて保持するデータ構造のこと。
配列は一つの要素にオブジェクトが一つ入っていたのに対して、ハッシュはオブジェクトがキーとバリューの二つ入っている。

ハッシュの定義のしかた

#例1
hash = Hash.new

#例2
user = {user1: "たけし", user2: "たかし"}

要素を追加する方法

型は以下の通り。

ハッシュ[追加したい要素のキー] = 

例えば

#以下の配列があったとして
users = {user1: "たけし", user2: "たかし"}

# キーがuser3、バリューがけんじの要素を追加するには
users[:user3] = "けんじ"
puts users # 出力結果  {:user1=>"たけし", :user2=>"たかし", :user3=>"けんじ"}

そのほかの追加の方法としては、以下があります。

ハッシュ.store(追加したい要素のキー, )

具体例は以下の通り。

#以下の配列があったとして
users = {user1: "たけし", user2: "たかし"}

# キーがuser3、バリューがけんじの要素を追加するには
users.store("user3", "けんじ")
puts users # 出力結果  {:user1=>"たけし", :user2=>"たかし", :user3=>"けんじ"}

要素を削除する方法

型は以下の通り。

ハッシュ.delete(削除したい要素のキー)

例えば

#以下の配列があったとして
users = {:user1=>"たけし", :user2=>"たかし", :user3=>"けんじ"}

# キーがuser3、バリューがけんじの要素を削除するには
users.delete("user3")
puts users # 出力結果  {:user1=>"たけし", :user2=>"たかし"}

値が(キーと一緒に)ハッシュから取り除かれる。

ハッシュの中の特定のバリューを取得する方法

型は以下の通り。

ハッシュオブジェクト[:キー名]

例えば

#以下の配列があったとして
users = {:user1=>"たけし", :user2=>"たかし"}

# "たかし"を取り出す時は
puts users[:user2]

ハッシュの中のバリュー一覧を取得する方法

例えば

#以下の配列があったとして
users = {:user1=>"たけし", :user2=>"たかし"}

puts users.values # 出力結果 たけし, たかし

ハッシュの中のキー一覧を取得する方法

例えば

#以下の配列があったとして
users = {:user1=>"たけし", :user2=>"たかし"}

puts users.keys # 出力結果 [:user1, :user2]

問題

配列の内部に、複数のユーザーの情報をハッシュとして持つ変数user_dataがある。
user_dataを利用して、全てのユーザーの名前だけが出力されるようにRubyでコーディングしてください。

user_data = [
             {user: {profile: {name: 'George'}}},
             {user: {profile: {name: 'Alice'}}},
             {user: {profile: {name: 'Taro'}}}
             ]

 解答

user_data.each do |u|
  puts u[:user][:profile][:name]
end

#または
user_data.each{ |u| puts u.dig(:user, :profile, :name) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロスサイトリクエストフォージェリ(CSRF)の対策

クロスサイトリクエストフォージェリ(CSRF)

Webサイトにスクリプトや自動転送(HTTPリダイレクト)を仕込むことによって、利用者に意図せず別のWebサイト上で何らかの操作(掲示板への書き込みや銀行口座への送金など)を行わせる攻撃手法のことをいいます。
CSRFの脆弱性が存在すると以下のような被害を被る可能性があります。
①利用者のアカウントによる物品の購入
②利用者の退会処理
③利用者のアカウントによる掲示板への書き込み
④利用者のパスワードやメールアドレスが変更
CSRF脆弱性の影響は「重要な処理」の悪用に限られるため、CSRFの脆弱性を個人情報の取得等に用いることはできません。

CSRFの攻撃例

例えば、利用者が罠サイトを閲覧することによってパスワードが変更されてしまう場合
①利用者がexample.jpにログインしている
②攻撃者は罠を作成
③利用者が罠を閲覧する
④罠のJavaScriptによる、被害者のブラウザ上で攻撃対象サイトに対し、新しいパスワードabcdefがPOSTメソッドにより送信される
⑤パスワードが変更される

CSRFの対策

CSRF攻撃を防ぐには、「重要な処理」に対するリクエストが利用者の意図によるものかどうかを確認することが必要になります。
このためCSRF対策が以下の2点です。
①CSRF対策の必要なページを区別する
②正規利用者の意図したリクエストを区別できるように実装する

CSRF対策の必要なページを区別する

CSRF対策はすべてのページに行う必要はありません。対策に必要なページは、他のサイトから勝手に実行されては困るようなページです。例えば、ECサイトの物品購入ページや、パスワード変更など個人情報の編集確定画面などです。
CSRFの対策としては、まず実装するWebアプリケーションのどのページに脆弱性対策が必要なのか設計段階で明らかにすることです。
例えば、機能一覧を紙に記入し、CSRFの対策が必要なページを色分けすると良いと思います。
商品ページ=>認証=>カートに追加=>購入確認=>購入確定
この中だと最後の購入確定がCSRFの対策が必要です。

正規利用者の意図したリクエストを区別できるように実装

CSRF対策で必要なことは、正規利用者の意図したリクエストなのかどうかということです。
意図したリクエストとは、利用者が対象のアプリケーション上で「実行」ボタンなどを押して、「重要な処理」のリクエストを発行することです。
正規のリクエストかどうかを判断する方法は3種類あります。
①秘密情報の埋め込み
②パスワードの再入力
③Refererのチェック

秘密情報の埋め込み

登録画面や注文確定画面などのCSRF攻撃への対策が必要なページに対して、第三者の不正利用者が知り得ない秘密情報を要求するようにすれば、不正リクエストによる重要な処理が実行されることはありません。このような目的で使用される秘密情報のことをトークンといいます。

パスワードの再入力

こちらは文字通り重要な処理が確定する前に、再度パスワードを入力してもらいます。これはCSRF対策の他にも物品の購入などに先立って、利用者の意思の念押しをしたり、共用のPCにおいて正規の利用者以外の利用者が、重要な処理を実行するのを防いだりする効果があります。
CSRFの攻撃例として、とりあげたパスワード変更ページにも現在のパスワードを再入力させることによりCSRF攻撃を防ぐことが可能です。

RailsでのCSRF対策方法

Rails側できちんと対策を行ってくれています。基本的には開発者はなにもしなくても大丈夫です。
例として、RailsでのCSRF対策

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception   #追加部分

  before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :nickname
  end
end

protect_from_forgery with: :exception
これがRailsアプリケーション内でCSRF対策を行うというという命令になります。こちらをすべてのコントローラの親であるapplication_controller.rbに記述することによって、その子コントローラすべてに先ほど説明したようなCSRF対策をRails側で行ってくれます。
具体的には、まずサイトのHTMLに一意のトークンを埋め込みます。これと同じトークンを、セッションcookie(クッキー)にも保存しています。ユーザーがPOSTリクエストを送信すると、HTMLに埋められているCSRFトークンも一緒に送信されます。あとは、サーバ側でページのトークンとセッション内のトークンを比較し、両者が一致することを確認したらリクエストを受け付けます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gRPC: prototoolからbufへの道 ~ vol. 1 ~

gRPC: prototoolからbufへの道 ~ vol. 1 ~

Bufとは

Protobufが技術的なメリットの良い選択になるだけでなく、非常に使いやすく決定が簡単になることです

機能

  • 自動ファイル検出
    • prototoolとは違い、任意に指定も可能
  • 正確なlintとbreaking checkersの構成が選択可能になる
    • lint: 40 breaking checkers: 50
  • エラー出力はどのエディターでも簡単に解析可能
  • コンパイルの高速化
    • protoc: 4.3sに対して 4コアでbuf: 0.8s
  • protocのプロトコルプラグインとして使用

Buf CLIツール

サポートされている機能

  • 優れたAPI設計の選択と構造を強制するリンター
  • ソースコードまたはワイヤレベルでの互換性を強制する重大な変更検出器
  • FileDescriptorSetsの拡張機能であるイメージを生成する構成可能なファイルビルダー

インストール方法

brew tap bufbuild/buf
brew install buf

使い方は次回紹介します

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザーの役割を分ける際はSQLアンチパターンを意識する

●はじめに

Railsチュートリアルを参考にしポートフォリオを作成すると、Userモデルにadmin属性を追加しSTIで管理ユーザーを作成する事になるかと思います。
しかし、SQL的にはアンチパターンであるため注意が必要です。

●結論

Userモデルと別に、adminモデルを作成する事が良い。

●STI(Single Table Inheritance:単一テーブル継承)

STIは、単一の継承階層に所属するクラス群を、ひとつのテーブルを使って永続化する手法です。

スクリーンショット 2020-04-07 20.20.10.png

このように実装する方法ですね。しかしこの方法はSQL的にアンチパターンだという事です。
なぜこの方法が良くないかという点は、下記URLを参考にしました。
https://qiita.com/yebihara/items/9ecb838893ad99be0561

短所
・特定のサブクラスに固有の属性に対してNOT NULL制約を適用できない
・特定のサブクラスのみを参照すべき他テーブルの外部キー制約が、誤ったサブクラスを参照することを防げない
・テーブルのカラム数が多くなりやすい
・一部のサブクラスでしか使われない列の値はNULLばかりとなり、見た目がスパースになる

●まとめ

データベース設計時には、アンチパターンを意識する必要がある。
Rails初学者にとってSQLやDBは苦手意識を持ちやすい所だと思います(自分がそうです)
だからこそポートフォリオ作成前にはしっかりとデータベース設計をし、ER図を作成し、メンターに確認をして頂いた後に実装を進めることをオススメします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロスサイトスクリプティング(XXS)対策

クロスサイトスクリプティング(XXS)

Webアプリケーションでは、外部からの入力などに応じて表示が変化するページを実装したいことがしばしばあります。
しかし、この部分のHTML生成の実装に問題があると、外部よりスクリプトを埋め込まれクッキーを盗まれたり、JavaScriptによる攻撃を受けてしまうおそれがあります。
こういった攻撃手法をクロスサイトスクリプティングと言います。

XSSの攻撃例

では、実際にXSSの脆弱性を用いた攻撃例です。
ここでは登場人物を用いて説明します。
ユウスケ・・・ 一般ユーザ
タカシ ・・・攻撃を仕掛ける悪意のある者
1. タカシはXSSの脆弱性があるサイトに悪意のあるスクリプトを埋め込む
2. タカシは1.でスクリプトを埋め込んだサイトをリンクに指定する罠サイトを用意する
3. タカシはユウスケに罠サイトへ誘導するようなメールを送信する
4. ユウスケは罠サイトにアクセスし、タカシがスクリプトを埋め込んだサイトにアクセス
5. ユウスケのブラウザ上でタカシが埋め込んだスクリプトが実行される
※ここで出てくる罠サイトとは、スクリプトを埋め込んだXSSの脆弱性のあるサイトへのリンクを含んでいるページのことをいいます。
ここで実行されるスクリプトの例としては、cookieを攻撃者のサーバに送信されてしまったり、マルウェア(悪意のあるソフトウェア)を仕込んであるサイトにリダイレクトされウイルスに感染させられたりとJavascriptで記述することが可能なすべての攻撃を受けてしまう可能性があります。

XSSの対策

XSSが発生する主要因として、フォームから入力されたHTMLタグがそのままページに反映されてしまっていることがあげられます。
したがって、XSSを防ぐためにはHTMLを生成する際に意味を持つ「"」や「<」を文字参照によってエスケープすることが基本となります。

文字参照

HTML上で直接記述できない特殊文字を表記する際に用いられる記法です。例えば、HTML中に「<」もしくは「>」と記述するとこの二つはタグの初め、終わりと認識されてしまいます。これでは文字列として上記の記号を用いることができません。そこで、文字参照を利用します。

変換前 変換後
< & lt;
> & gt;
& & amp;
" & quot;
' & #39;

上の表は、XSSを対策する際にエスケープすべき特殊文字の一覧です。これらの特殊文字を文字参照に変換して保存すれば、外部から埋め込まれたスクリプトが実行されることはありません。

rawメソッド

文字列を文字参照にエスケープしないためのヘルパーメソッドです。
【例】

raw(文字列)
<%= raw(tweet.text) %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

each文の基本1

環境,前提

Ruby 2.5.1
MacOS Mojave Ver.10.14.6

本記事はRubyがインストールされた前提の記事です。
Rubyをインストールしたあと、とにかくRubyをいろいろ触ってみて慣れていくための記事です。お役に立てば幸いです。

eachメソッド

eachメソッドは配列や範囲オブジェクトで使用できるメソッドで、オブジェクトに含まれる要素を順番に取得することができます。基本的な書き方は以下のようになります。

sample.rb
配列オブジェクト.each do |変数| 
 #処理 
end 

具体例で書きますと

sample.rb
fruites = ["オレンジ", "イチゴ", "リンゴ"]
fruites.each do |fruite|
 puts fruite
end

出力結果は以下のようになります。

Tarminai
オレンジ
イチゴ
リンゴ

解説

まずソースコードの1行目でfruitesという配列オブジェクトを用意しています。

そして配列オブジェクトfruitesの要素(オレンジ、イチゴ、リンゴ)を順番に変数fruiteに代入し、putsで出力しています。

配列の名前は複数形なのでfruites,eachメソッド内での変数は単数形のfruiteになっています。

また、eachメソッド内で使用したfruiteという変数は「ブロックパラメーター」と呼ばれています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

each文の基本

環境,前提

Ruby 2.5.1
MacOS Mojave Ver.10.14.6

本記事はRubyがインストールされた前提の記事です。
Rubyをインストールしたあと、とにかくRubyをいろいろ触ってみて慣れていくための記事です。お役に立てば幸いです。

eachメソッド

eachメソッドは配列や範囲オブジェクトで使用できるメソッドで、オブジェクトに含まれる要素を順番に取得することができます。基本的な書き方は以下のようになります。

sample.rb
配列オブジェクト.each do |変数| 
 #処理 
end 

具体例で書きますと

sample.rb
fruites = ["オレンジ", "イチゴ", "リンゴ"]
fruites.each do |fruite|
 puts fruite
end

出力結果は以下のようになります。

Tarminai
オレンジ
イチゴ
リンゴ

解説

まずソースコードの1行目でfruitesという配列オブジェクトを用意しています。

そして配列オブジェクトfruitesの要素(オレンジ、イチゴ、リンゴ)を順番に変数fruiteに代入し、putsで出力しています。

配列の名前は複数形なのでfruites,eachメソッド内での変数は単数形のfruiteになっています。

また、eachメソッド内で使用したfruiteという変数は「ブロックパラメーター」と呼ばれています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

情報セキュリティとは

情報セキュリティ

Webサービスにおいてのセキュリティ(安全保障)です。情報セキュリティにおける理想は、「不正なアクセスや情報の漏洩を防ぎつつ、権限がある人は便利に利用できる」状態を維持することです。「機密性」「完全性」「可用性」の3つの要素を維持することを目標にします。
①機密性 権限を持たない人が情報資産を見たり使用できないようにすること
②完全性 権限を持たない人が情報を書き換えたり消したりできないようにすること
③可用性 権限を持つ人がいつでも利用したいときに利用できるようにすること

脆弱性(ぜいじゃくせい)について

コンピュータやネットワーク、アプリケーション全体のセキュリティに弱点を作り出すコンピュータソフトウェアの欠陥や仕様上の問題点のことを脆弱性と言います。Webアプリケーションに脆弱性があると、開発者側だけでなく、利用者側も被害を被る可能性があり、様々な被害が生じる可能性があります。脆弱性は、バグや、開発者のセキュリティチェック不足により生まれます。

脆弱性によってもたらされる被害

脆弱性がアプリケーション内に存在することによって、以下のような被害が想定されます。
①個人情報を勝手に閲覧される(機密性侵害)
②Webページの内容が改ざんされる(完全性侵害)
③Webページ自体が利用不能になる(可用性侵害)
このような問題が起きてしまうと、利用者への金銭的損失の補填や補償、開発者の社会的信頼の失墜による売上の減少、Webサイト停止による機会損失など多くの被害をもたらすので脆弱性対策は必須です。

脆弱性が生まれる理由

一つは、バグによるもの
二つ目は、開発者側のセキュリティチェック不足によるもの
ここでいうバグとは、クロスサイトスクリプティングのように投稿フォーム等にscriptタグでJavascriptのコードを記述・送信することで、それがページに埋め込まれ、実行されてしまうといったようなプログラミング言語の仕様に起因するものようなことを言います。

クロスサイトスクリプティング(XSS)

Webサイトに利用されるアプリケーションの脆弱性もしくはその脆弱性を悪用した攻撃のことです。特にWeb閲覧者側が制作することのできる動的サイト(例:TwitterなどのSNS、掲示板等)に対して、その脆弱性を利用して悪意のある不正なスクリプトを挿入することによりその発生するサイバー攻撃です。

HTTP

HTTPとはWebブラウザとWebサーバの間でHTMLや画像ファイルなどのコンテンツの送受信に用いられる通信プロトコルです。Webページを閲覧・利用することができるのもHTTPという仕組みがあるからです。

プロトコル

複数のユーザが滞りなく信号やデータ、情報を相互に伝送できるよう、あらかじめ決められた約束事や手順の集合のことです。
例えば人と人との会話を例とすると、片方が日本語で話し、もう片方が中国語で話していては会話にならない状態です。
そこで、会話を成立させるためには使用言語をきちんと決める必要があります。この使用言語を何にするか決める役割が通信におけるプロトコルにあたります。
HTTPはクライアント(自分のパソコンなど)、ブラウザからのリクエストに対して、サーバからのレスポンスが返ってくることによって実現します。
例えば飲食店で考えたときに、料理を注文します。すると、お店側は注文された料理を提供します。
HTTPもこれと同じでクライアントが要求したページをサーバ側がクライアントに合わせて提供するという仕組みになっています。
例として、クライアントとサーバー間でHTTP通信を行う際はメッセージのやり取りを行います。
まず、クライアントからサーバーに対して以下のようなリクエストメッセージを送信します。

HTTPリクエスト
GET / HTTP/1.1 リクエストライン
Accept: image/gif, image/jpeg, / ヘッダ
Accept-Language: ja
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (Compatible; MSIE 6.0; Windows NT 5.1;)
Host: www.xxx.zzz
Connection: Keep-Alive
(空行)
メッセージボディ(POSTメソッドなどで使用)

www.xxx.zzz というアドレス
に対してHTTP/1.1というバージョンを使い、GETメソッドで"/"パスにアクセスしたい、というメッセージをクライアントからサーバに送っているいう意味になります。
それに対してサーバは以下のようなレスポンスメッセージを返します。

HTTPレスポンス
HTTP/1.1 200 OK ステータスライン
Date: Sun, 11 Jan 2004 16:06:23 GMT ヘッダ
Server: Apache/1.3.22 (Unix) (Red-Hat/Linux)
Last-Modified: Sun, 07 Dec 2003 12:34:18 GMT
ETag: "1dba6-131b-3fd31e4a"
Accept-Ranges: bytes
Content-Length: 4891
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html
(空行)
htmlやcssの情報(メッセージボディ)

ステータスラインを見てみると先ほどのリクエストメッセージに対して、HTTP1.1/(バージョン)200(ステータス番号)OK(補足メッセージ)と返しています。
これは「HTTP/1.1というバージョン下でリクエストを受け付けました」という意味を表しています。

ステータスコード

HTTP通信のレスポンスメッセージのステータスライン中にある3桁の数字で、クライアントからのリクエストに対して、サーバからの返
100の位に意味があり、そちらで分類されます。

ステータスコード 意味
100番台 処理が継続中
200番台 正常終了
300番台 リダイレクト
400番台 クライアント側でのエラー
500番台 サーバー側でのエラー

よく使われるステータスコードとしては、200(正常終了)、301及び302(リダイレクト)、404(ファイルが存在しない)、500(内部サーバーエラー)などがあります。

セッション/クッキー(cookie)

HTTPは、ステートレスな通信となっています。
ステートレスとはサーバが現在の状態を保持せず、ユーザの入力の内容のみによって出力が決定される状態のことです。
例えば、ユーザのログイン状態やECサイトのカート機能などがあります。これらは、ページが遷移してもユーザの情報や購入しようとしている商品の情報を保持している必要があります。
こういった状態を保持するために用いられるのがセッションであり、HTTP通信のセッションを管理するために作られた仕組みをクッキー(cookie)といいます。このような通信をステートフル通信といいます。

セッション

複数回に渡るリクエストにおいて、クライアントを特定するための仕組みです。具体的には、クライアントは初回のリクエストで自身を識別させるIDをサーバーに渡します。以降、サーバーはそのIDを持ってクライアントを認識します。

クッキー(cookie)

クライアント側のブラウザに保持することができる情報のことです。通常、初回の通信でサーバーがクライアントにクッキーとしてセッションIDを保持させ、以降クライアントはそれを用いてサーバーに対して自身を特定させます。
実際にクッキー(cookie)がどのように使われているか
まず、サーバー側で明示的にクッキー(cookie)を設定しますよ、という宣言をします。
Railsでは、session_store.rbというファイル内でセッションの管理方法を指定します。デフォルトでクッキー(cookie)を利用する設定になっているため、Railsでは意識せずクッキー(cookie)でのセッション管理を行うことができます。

config/initializer/session_store.rb
TechReviewSite::Application.config.session_store :cookie_store, key: '_tech_review_site_session'

ログイン処理を例にとると、以下のような流れでクッキー(cookie)が利用されます。
①クライアントはログイン画面でIDとパスワードを入力する
②すると、サーバでクッキー(cookie)が生成され、クライアントが保持する
③次回以降アクセスする際に、クライアントが保持しているクッキー(cookie)がサーバに送信される
④サーバはこのクッキー(cookie)値を元にクライアントを識別し、ログイン作業を省く
まとめると
HTTPとはWebアプリケーションを利用する際にクライアントとサーバ間の情報をやり取りするための通信プロトコル
セッションとは、Webアプリケーション上で前のページの状態を保持するために利用される機能

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

環境構築から始めるテスト駆動開発 ~Ruby開発環境を構築する(WSL版)~

環境構築から始めるテスト駆動開発 ~Ruby開発環境を構築する(WSL版)~

はじめに

これは 環境構築から始めるテスト駆動開発 ~プログラミング環境の共通基盤を構築する~ の開発言語セットアップ記事です。Windows 10 Home で共通基盤が構築されていることを前提としています。

インストール

Ruby開発環境の自動構築をするため以下のレポジトリを自分のレポジトリにフォークします。

テスト駆動開発から始めるRuby入門

Fork を押します。

provision 001

Fork が完了して自分のレポジトリにコピーされたら Clone or download を押してレポジトリのURLをコピーします。

provision 002

エクスプローラアイコンメニューから レポジトリをクローンする を押します。

provision 003

先程コピーしたレポジトリのURLを貼り付けます。

provision 004

保存先はそのままで OK を押します。

provision 005

開く を押します。

provision 006

メニューから ターミナル 新しいターミナル を選択します。

provision 007 1

provision 007 2

ターミナルに以下のコマンドを入力します。実行時にパスワード入力が求められるのでWSLで設定したパスワードを入力してください。

$ sudo apt-get update -y
[sudo] password for newbie4649:

provision 008

続いて、ターミナルに以下のコマンドを入力します。

$ sudo apt install ansible -y

続いて、エクスプローラから provisioning/vars/site.yml をファイルを開いて user: の名前をWSLで設定したユーザーIDに変更します。

provision 009

変更を保存したらターミナルに以下のコマンドを入力します。

$ cd provisioning/tasks/
$ sudo ansible-playbook --inventory=localhost, --connection=local site.yml

provision 010

セットアップが完了したらエディタを再起動してプロジェクトを開きます。

provision 010 2

以下のコマンドを入力してRubyがセットアップされていることを確認します。

$ ruby -v

provision 011

続いて、ターミナルに以下のコマンドを入力します。

$ code ~/.bashrc

表示されたファイルの一番最後に以下のコードを追加して保存します。

...
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_compl

provision 012

保存したら以下のコマンドを実行してNode.jsのバージョンが表示されたらセットアップ完了です。

$ source ~/.bashrc
$ nvm install --lts
$ node -v

provision 013

追加パッケージのインストール

Ruby for Visual Studio Code

Ruby Solargraph

vscode-endwise

ruby-rubocop

Test Explorer UI

Ruby Test Explorer

ターミナルに以下のコマンドを入力します。

gem install rubocop
gem install debase
gem install ruby-debug-ide
gem install solargraph

Hello world

プログラムを作成する

REAMD.md を選択してから 新しいファイル 作成アイコンを押します。

ruby hello 001

ファイル名は main.rb とします。

ruby hello 002

ファイルに以下のコードを入力したらRunアイコンを選択して create a launch.json file を押してメニューからRubyを選択します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, false)
  end
end

ruby hello 003

Debug Local File を選択します。

ruby hello 004

launch.json ファイルが作成されたら main.rb タブに戻ってF5キーを押します。

ruby hello 005

デバッグコンソールに実行結果が表示されれば準備完了です。

ruby hello 006

テストをパスするようにコードを修正してF5キーを押します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

end

ruby hello 007

テスティングフレームワークの動作が確認できたので hello_world 関数の作成に入ります。まず以下のコードを追加してF5キーを押してテストが失敗することを確認します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end
end

ruby hello 008

hello_world 関数を追加してテストをパスさせます。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end
end

def hello_world
  'Hello from Ruby'
end

ruby hello 009

指定された名前で挨拶を返すようにします。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world
  "Hello from Ruby"
end

ruby hello 010

関数に引数を追加します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name)
  "Hello from #{name}"
end

ruby hello 011

指定された名前で挨拶を返す テストはパスしましたが今度は 簡単な挨拶を返す テストが失敗するようになりましたのでデフォルト引数を設定してテストをパスするようにします。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name = 'Ruby')
  "Hello from #{name}"
end

ruby hello 012

仕上げに不要なテストを削除してテストケースの文言をわかりやすくしておきます。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何も指定されていない場合は既定の挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name = 'Ruby')
  "Hello from #{name}"
end

ruby hello 013

プログラムをデバッグする

まず確認したいプログラムの行を左部分を押してブレークポイント(赤丸)を設定します。

ruby debug<br>
001

ブレークポイントを設定したらF5を押してプログラムの実行します。そうするとブレークポイント部分でプログラムが停止して変数などの情報が確認できるようになります。

ruby debug 002

画面上の実行ボタンを押すと次のブレークポイントに移動します。

ruby debug 003

デバッガを終了するには終了ボタンを押します。

ruby debug 004

ブレークポイントを再度押すことで解除ができます。

ruby debug 005

プログラムをレポジトリに保存する

全ての変更をステージ を選択します。

ruby git 001

変更内容に feat: HelloWorld と入力して コミット を押します。

ruby git 002

変更内容は GitLens から確認できます。

ruby git 003

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

環境構築から始めるテスト駆動開発 ~Ruby開発環境を構築する~

環境構築から始めるテスト駆動開発 ~Ruby開発環境を構築する~

はじめに

これは 環境構築から始めるテスト駆動開発 ~プログラミング環境の共通基盤を構築する~ の開発言語セットアップ記事です。Windows 10 Home で共通基盤が構築されていることを前提としています。

インストール

RubyInstallerからWITH DEVKITをインストールします。

ruby win install 001

インストラーの指示に従います。

ruby win install 002

ruby win install 003

ruby win install 004

ruby win install 005

3を入力してエンターキーを押します。

ruby win install 006

追加パッケージのインストール

Ruby for Visual Studio Code

Ruby Solargraph

vscode-endwise

ruby-rubocop

Test Explorer UI

Ruby Test Explorer

設定

既定のシェルをPowerShell Coreに変更します。

ruby win vscode 001

ruby win vscode 002

新しいターミナルを開いて以下のコマンドを入力します。

gem install rubocop
gem install debase
gem install ruby-debug-ide
gem install solargraph

ruby win vscode 003

ruby win vscode 004

Hello world

プログラムを作成する

Projects フォルダ内に Ruby フォルダを作成してエディタからフォルダを開きます。

ruby win hello 001

ruby win hello 002

ruby win hello 003

新しいファイル 作成アイコンを押します。

ruby win hello 004

ファイル名は main.rb とします。

ruby win hello 005

ファイルに以下のコードを入力したらRunアイコンを選択して create a launch.json file を押してメニューからRubyを選択します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, false)
  end
end

ruby win hello 006

Debug Local File を選択します。

ruby win hello 007

launch.json ファイルが作成されたら main.rb タブに戻ってF5キーを押します。

ruby win hello 008

デバッグコンソールに実行結果が表示されれば準備完了です。

ruby win hello 009

テストをパスするようにコードを修正してF5キーを押します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

end

ruby win hello 010

テスティングフレームワークの動作が確認できたので hello_world 関数の作成に入ります。まず以下のコードを追加してF5キーを押してテストが失敗することを確認します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end
end

ruby win hello 011

hello_world 関数を追加してテストをパスさせます。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end
end

def hello_world
  'Hello from Ruby'
end

ruby win hello 012

指定された名前で挨拶を返すようにします。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world
  "Hello from Ruby"
end

ruby win hello 013

関数に引数を追加します。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name)
  "Hello from #{name}"
end

ruby win hello 014

指定された名前で挨拶を返す テストはパスしましたが今度は 簡単な挨拶を返す テストが失敗するようになりましたのでデフォルト引数を設定してテストをパスするようにします。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何か便利なもの
    assert_equal(true, true)
  end

  def test_簡単な挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name = 'Ruby')
  "Hello from #{name}"
end

ruby win hello 015

仕上げに不要なテストを削除してテストケースの文言をわかりやすくしておきます。

require 'minitest/autorun'

class TestHelloWorld < Minitest::Test
  def test_何も指定されていない場合は既定の挨拶を返す
    assert_equal('Hello from Ruby', hello_world)
  end

  def test_指定された名前で挨拶を返す
    assert_equal('Hello from VSCode', hello_world('VSCode'))
  end
end

def hello_world(name = 'Ruby')
  "Hello from #{name}"
end

ruby win hello 016

プログラムをデバッグする

まず確認したいプログラムの行を左部分を押してブレークポイント(赤丸)を設定します。

ruby win debug 001

ブレークポイントを設定したらF5を押してプログラムの実行します。そうするとブレークポイント部分でプログラムが停止して変数などの情報が確認できるようになります。

ruby win debug 002

画面上の実行ボタンを押すと次のブレークポイントに移動します。

ruby win debug 003

デバッガを終了するには終了ボタンを押します。

ruby win debug 004

ブレークポイントを再度押すことで解除ができます。

ruby win debug 005

プログラムをレポジトリに保存する

ソース管理を選択して リポジトリを初期化する を押します。

ruby win git 001

全ての変更をステージ を選択します。

ruby win git 002

変更内容に feat: HelloWorld と入力して コミット を押します。

ruby win git 003

変更内容は GitLens から確認できます。

ruby win git 004

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Ajaxを用いた非同期投稿の実装

目標

ezgif.com-video-to-gif (1).gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

ログイン機能を実装済み。

ログイン機能 ➡︎ https://qiita.com/matsubishi5/items/5bd8fdd45af955cf137d

投稿機能の実装

テーブル

schema.rb
ActiveRecord::Schema.define(version: 2020_04_05_115005) do

    create_table "books", force: :cascade do |t|
        t.string "title"
        t.text "body"
        t.integer "user_id"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false
    end

    create_table "users", force: :cascade do |t|
        t.string "email", default: "", null: false
        t.string "encrypted_password", default: "", null: false
        t.string "reset_password_token"
        t.datetime "reset_password_sent_at"
        t.datetime "remember_created_at"
        t.integer "sign_in_count", default: 0, null: false
        t.datetime "current_sign_in_at"
        t.datetime "last_sign_in_at"
        t.string "current_sign_in_ip"
        t.string "last_sign_in_ip"
        t.string "name"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false

        t.index ["email"], name: "index_users_on_email", unique: true
        t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
    end
end

モデル

user.rb
class User < ApplicationRecord
    has_many :books, dependent: :destroy
end
book.rb
class Book < ApplicationRecord
    belongs_to :user
end

ルーティング

routes.rb
Rails.application.routes.draw do
    devise_for :users
    resources :users
    resources :books
end

コントローラー

books.controll.rb
class BooksController < ApplicationController
    def create
        book = Book.new(book_params)
        book.user_id = current_user.id
        if book.save
            redirect_to book, notice: "successfully created book!"
        else
            @books = Book.all.order(created_at: :desc) #降順
            render 'index'
        end
    end

  def index
    @book = Book.new
    @books = Book.all.order(created_at: :desc) #降順
  end

    private
        def book_params
            params.require(:book).permit(:title, :body)
        end
end

ビュー

books/index.html.erb
<% if @book.errors.any? %>
    <h2><%= @book.errors.count %>error</h2>
    <div>
        <ul>
            <% @book.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>

<%= form_with model: @book do |f| %>
    <div class="field row">
            <%= f.label :title %>
            <%= f.text_field :title, class: "col-xs-12 book_title" %>
    </div>

    <div class="field row">
        <%= f.label :body %>
        <%= f.text_area :body, class: "col-xs-12 book_body" %>
    </div>

    <div class="actions row">
            <%= f.submit class: "btn btn-primary col-xs-12" %>
    </div>
<% end %>
<h2>Books index</h2>
<table class="table table-hover table-inverse">
    <thead>
        <tr>
            <th></th>
            <th>Title</th>
            <th>Opinion</th>
        </tr>
    </thead>
    <tbody>
        <% @books.each do |book| %>
            <tr>
                <td>
                    <%= link_to book.user do %>
                         <%= attachment_image_tag book.user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg" %>
                    <% end %>
               </td>
                <td><%= link_to book.title, book %></td>
                <td><%= book.body %></td>
            </tr>
        <% end %>
    </tbody>
</table>

非同期機能の実装

1. jQueryの導入

Gemfile
    gem 'jquery-rails'
ターミナル
    $ bundle
application.js
    //= require rails-ujs
    //= require activestorage
    //= require turbolinks
    //= require jquery
    //= require_tree .

2. booksコントローラーのcreateアクションを編集

books.controller.rb
class BooksController < ApplicationController
#ローカル変数をインスタンス変数に変更
    def create
        @book = Book.new(book_params)
        @book.user_id = current_user.id
        unless @book.save
            render 'index'
        end
    end
end

3. フォームを編集する

非同期投稿を行うときは必ず「form_with」を使用する。

※「form_for」「form_tag」だとレイアウトが崩れた。

books/index.html.erb
<!-- form_withの引数に「remote: true」を追記 -->
<%= form_with model: @book, remote: true do |f| %> 
    <div class="field row"> 
            <%= f.label :title %>
            <%= f.text_field :title, class: "col-xs-12 book_title" %>
    </div>

    <div class="field row">
        <%= f.label :body %>
        <%= f.text_area :body, class: "col-xs-12 book_body" %>
    </div>

    <div class="actions row">
            <%= f.submit class: "btn btn-primary col-xs-12" %>
    </div>
<% end %>

4. 投稿一覧を編集する

books/index.html.erb
<tbody class="new_book"> <!-- 投稿一覧の親要素にクラスをつける -->
    <% @books.each do |book| %>
        <%= render 'books', book: book %> <!-- 投稿一覧をパーシャル化 -->
    <% end %>
</tbody>

books/_books.html.erb
<tr>
    <td>
        <%= link_to book.user  do %>
            <%= attachment_image_tag book.user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg" %>
        <% end %>
    </td>
    <td><%= link_to book.title, book %></td>
    <td><%= book.body %></td>
</tr>

5. JavaScriptファイルの作成

books/create.js.erb
$(".book_title").val('');
$(".book_body").val('');
$(".new_book").prepend("<%= j(render 'books', book: @book) %>");

$(".new_book")
➡︎ 「4」で付けたクラスを指定

.prepend("<%= j(render 'books', book: @book) %>");
➡︎ 既存投稿の先頭に新規投稿を表示

$(".book_title").val('');
$(".book_body").val('');
➡︎ 入力フォームを空にする

参考サイト

https://qiita.com/hiro266/items/56ec2c350fd9d8ca22d8

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでERBを直接使い、文字列として結果を取得する

はじめに

Railsのviewではなく、erbの結果を文字列としてほしいケースが有ったので、その方法です。

パスにRails固有のコードを使っていますが、そこ以外はRailsじゃなくても使えます。

ERBのインスタンス生成

file = File.read(Rails.root.join('app', 'views', 'hoge', 'fuga.html.erb').to_s)
erb = ERB.new(file)

といった形でERBのインスタンスを生成できます。

Railsじゃない場合は、

file = File.read('/path/to')

みたいな感じですね。

結果の取得

result_text = erb.result_with_hash(hoge:1, fuga:2)

で結果の取得ができます。

引数に渡したハッシュが、そのままerb内でkeyを変数名、valueを値とするローカル変数として読み込まれます。

あとがき

erbを直接使いたい状況、そこまで多くはないとは思いますが、もし必要な方の役に立てば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails5】rails_adminで多対多の関連付けを上手く編集する方法

はじめに

一瞬でめちゃイケてる管理画面を作ってくれるrails_admin
今までCRUDそれぞれ画面編集してたのはなんだったんだ……と思う完成度です。

しかし、多対多の関連付けの時、毎回のようにエラーが出ました。

出たエラー

ActiveRecord::HasManyThroughOrderError in RailsAdmin::Main#edit

原因

色々試しましたが、Modelでの関連付け設定の順番が原因でした。

構成

タイトルごとに複数のタグ。
タグごとに複数のタイトル。

title has_many tags
tag has_many titles

これを実現させるために、tag_mapsというテーブルを間に挟んでいます。

[tag_maps]
id
title_id
tag_id

解決したコード

model/title.rb
class Title < ApplicationRecord
  has_many :tag_maps
  has_many :tags, through: :tag_maps
end
model/tag_map.rb
class TagMap < ApplicationRecord
  belongs_to :title
  belongs_to :tag
end
model/tag.rb
class Tag < ApplicationRecord
  has_many :tag_maps
  has_many :titles, through: :tag_maps
end

ただ普通に記述してるだけなのですが、なぜか順番が逆だとエラーが出ました。

オマケの注意点

今回のこれは、Rails5での解決策でした。
(Rails 5.2.4.2)

他のバージョンでは、ここ見たら参考になるかも……?
https://github.com/sferik/rails_admin/wiki/Associations-basics

あとrails_adminの編集画面で若干表示項目が変に見えるのは……まぁええでしょ。エラー出てないし

終わり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]モデル、テーブル、カラムの作成と削除コマンド

本記事について

開発して行く中で
「あれ?マイグレーションファイルの作成コマンドってこんなんだっけ。」
のように、ド忘れしてしまうことがあります。
そのため、メモ代わりに書きたいと思います。

DB関係

モデル & テーブル作成

モデル & マイグレージョンファイル作成

rails g model モデル名(単数)

例 : userモデル
rails g model user

補足ですが上のコマンドで以下が作成されます。
1. モデルのクラスファイル
2. マイグレーションファイル
3. モデルの自動テスト
4. モデルの自動テストで使うfictureファイル

実行

bundle install

モデル削除

rails d model モデル名(単数)

テーブルの削除

主な流れ

- マイグレーションファイル作成
- マイグレーションを編集
- 実行

例: users テーブルの削除
マイグレージョンファイルの作成

rails g migration DropTableUsers

マイグレーションファイルの編集

2020xxxxx.rb
class DropTableUsers < ActiveRecord::Migration

  def change
    drop_table :users
  end

end

実行

bundle install

既存のテーブルにカラムを追加

rails g Addカラム名Toテーブル名 カラム名:カラム型
の形でターミナルに入力しマイグレーションファイルを作成します。

例: usersテーブルにstring型nameカラムを追加

rails g migration AddNameToUsers name:string

実行

bundle install

既存のテーブルのカラムを削除

rails g migration Removeカラム名Fromテーブル名 カラム名:テーブル名
の形でターミナルに入力しマイグレーションファイルを作成します。

例: usersテーブルのstring型nameカラムを削除

rails g migration RemoveNameFromUsers name:string

実行

bundle install

おわり

最後まで見ていただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】マイグレーションファイルやschemaファイルの仕組み、rails db:migrate、rails db:rollbackとかを纏めてみた

マイグレーションファイルとは?

マイグレーションファイルとはデータベースの設計図のことです。

このマイグレーションファイルをどのように作成して、データベースに反映させるのか、
また、一度データベースに反映させた内容をどのように修正できるのか、自分なりに纏めてみました。

マイグレーションファイルを作成し、データベースに反映させる

まずはターミナルでマイグレーションファイル(DBの設計図)を作成します。
僕の場合、text型のbodyカラムを持つ、BoardモデルとUserモデのマイグレーションファイルを作成しました。

まずはreference型にboard_id:referencesuser_id:referencesと間違った内容を付けたとします。

# 誤った内容であることに注意!
rails g model comment body:text board_id:references user_id:references

これによって、以下のマイグレーションファイルが作成される。

db/migrate/20200330045356_create_comments.rb
# 誤ったファイルであることに注意!
class CreateComments < ActiveRecord::Migration[5.2]
  def change
    create_table :comments do |t|
      t.text :body, null: false
      t.references :board_id, foreign_key: true
      t.references :user_id, foreign_key: true

      t.timestamps
    end
  end
end

このマイグレーションファイルを作成した段階では、データベースに反映されていません。
ターミナルで下記のrails db:migrateコマンドを実行すると、作成したマイグレーションファイルが読み込まれ、データベースに反映されます。

> rails db:migrate
== 20200330045356 CreateComments: migrating ===================================
-- create_table(:comments)
   -> 0.0143s
== 20200330045356 CreateComments: migrated (0.0165s) ==========================

データベースに反映されているマイグレーションファイルを確認する

そして、rails db:migrate:statusを実行すると、DBに反映されたマイグレーションファイルを確認できます。
upと書いているファイルがデータベースに反映されているもの(マイグレーション済みということ)です。

> rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200310093526  Sorcery core
   up     20200316101344  Create boards
   up     20200316105948  Add user id to boards
   up     20200328064702  Add board image to board
   up     20200330045356  Create comments

次に、スキーマファイルで現在のデータベースの構造を確認できるのですが、
下記のcommentsテーブルはboard_id_iduser_id_idと誤ったカラムが追加されているので、修正したいです。

db/schema.rb
# 誤ったテーブル内容
create_table "comments", force: :cascade do |t|
    t.text "body", null: false
    t.integer "board_id_id"
    t.integer "user_id_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["board_id_id"], name: "index_comments_on_board_id_id"
    t.index ["user_id_id"], name: "index_comments_on_user_id_id"
  end

データベース及びマイグレーションファイルの修正方法

まずは、rails db:rollbackで最新のマイグレーションファイルをdown状態にします。
down状態のマイグレーションファイルは、データベースに反映されていない状態にあるということです。
(up状態にあるマイグレーションファイルを削除・編集することは避けましょう)

> rails db:rollback

== 20200330045356 CreateComments: reverting ===================================
-- drop_table(:comments)
   -> 0.0044s
== 20200330045356 CreateComments: reverted (0.0079s) ==========================

rails db:migrate:statusで、以下の様にdown状態になったことが確認できますね。

> rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200310093526  Sorcery core
   up     20200316101344  Create boards
   up     20200316105948  Add user id to boards
   up     20200328064702  Add board image to board
  down    20200330045356  Create comments

次に行う手順として、2通り方法があります。
①down状態にしたマイグレーションファイルを削除し、新しいマイグレーションファイルを作成してから、rails db:migrateする。
②down状態にしたマイグレーションファイルを直接編集し、rails db:migrateする。

どちらでもいいのですが、今回はカラム名を修正するだけなので、②の方法を取ってみます。
board_idboardに、user_iduserに変更します。

db/migrate/20200330045356_create_comments.rb
# 誤ったファイルであることに注意!
class CreateComments < ActiveRecord::Migration[5.2]
  def change
    create_table :comments do |t|
      t.text :body, null: false
      t.references :board, foreign_key: true
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end

これでrails db:migrateすると、マイグレーションファイルがup状態に戻ります。

> rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200310093526  Sorcery core
   up     20200316101344  Create boards
   up     20200316105948  Add user id to boards
   up     20200328064702  Add board image to board
   up     20200330045356  Create comments

スキーマファイルを確認すると、

db/schema.rb
create_table "comments", force: :cascade do |t|
    t.text "body", null: false
    t.integer "board_id"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["board_id"], name: "index_comments_on_board_id"
    t.index ["user_id"], name: "index_comments_on_user_id"
  end

無事、正しいカラム名を持ったテーブルがデータベースに反映されました!

まとめ

最後に、ここまでの内容をまとめておきます!

  • マイグレーションファイルとはデータベースの設計図のこと。
  • rails g model モデル名 カラム名:型で、モデルとマイグレーションファイルを作成する
  • rails db:migrateで、マイグレーションファイルをDBに反映させる(up状態)
  • rails db:rollbackで、マイグレーションファイルをDBに反映させる前の状態に戻す(down状態)
  • rails db:migrate:statusで、各マイグレーションファイルのDBへの反映状態(up,down)を確認できる
  • マイグレーションファイルの修正方法は、①down状態にしたマイグレーションファイルを削除し、新しいマイグレーションファイルを作成してから、rails db:migrateする。もしくは、 ②down状態にしたマイグレーションファイルを直接編集し、rails db:migrateする。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアルメモ - 第14章

メモの目次記事はこちら

公式Railsチュートリアル第14章へのリンク

サマリ

  • モデルの複雑な関連付けの方法
    • 1つのテーブルrelationshipを使って、followerfollowingを管理する方法
  • ネストしたルーティングの実装
  • ajaxの実装

ポイント

テーブル名と異なるカラムをFKとして使用したい場合

1対多の1側

  # user.rb
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy

1対多の多側

  # relationship.rb
  # follower_idカラムが存在している必要がある
  belongs_to :follower, class_name: "User"

has_manybelongs_toについて

has_manyで指定できるパターン

  # モデルの複数形 e.g. microposts
  has_many :microposts
  # 別名のシンボル、クラス名、FK
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id"
  • 関連付けとは別に、has_many :hoge, through: :fuga, source: :foobarを定義しておくと、こ配列``

belongs_toで指定できるパターン

デフォルトでは外部キーの名前を_idといったパターンとして理解し、 に当たる部分からクラス名を推測する
1. モデルのシンボル
2. 別名とクラス名

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

ルーティングのカスタマイズ - ネストしたURLの設定

  # /users/1/following や /users/1/followers 
  resources :users do
    member do
      get :following, :followers
    end
  end

  # /users/tigers
 resources :users do
    collection do
      get :tigers
    end
  end

Ajaxの実装方法

  • ローカル変数ではなくインスタンス変数への変更が必要
  • viewにはform_forの引数に, remote: trueを追加する
  • controllerにはrespond_toを追加する
  def create
    @user = User.find(params[:followed_id])
    current_user.follow(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

アンパサンド(&)を使ったブロックの短縮表記

# 以下は同値
[1, 2, 3, 4].map { |i| i.to_s }

[1, 2, 3, 4].map(&:to_s)

感想

  • めちゃくちゃ時間がかかったがなんとか完走することができた
  • チュートリアルとはいえtwitter風の動くアプリが完成したのは嬉しい
  • まだまだ理解しきったといえる状態ではないので、3周目4週目をやりながら使いこなしていきたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]JS(jquery)が動かない 初歩的ミス

jquery使用の際、同じコード引用下のに別アプリで
なぜか動かない。
と言う原因の一例です。

確認した場所

■Gemfile→jqueryがインストールされているか
■application.jsにjqueryの記述があるか。
■js内にturbolinks:loadが入っているかどうか

原因

コメントアウトされているコードの並び順
*これ、すごく大事でした。
初歩の初歩でテキストに書いてあった気がする。。。

//= require_tree .が、最初はこの位置におりました。
image.png
移動!!最下部へ
image.png
こちらでjqueryが動かない問題は解決いたしました。

require_tree .とは?

参考資料によると以下の記載があります(ちなみにrequire_treeはcssにも記述あります)
そしてアセットパイプラインと言うそうです。
application内にあるcssやjsの読み込み順を司るものです。

require_treeディレクティブは、指定されたディレクトリ以下の
すべての JavaScriptファイルを再帰的にインクルードし、出力に含めます。
このパスは、マニフェストファイルからの相対パスとして指定する必要があります。
require_directoryディレクティブを使用すると、指定されたディレクトリの
直下にあるすべてのJavaScriptファイルのみをインクルードします。
この場合サブディレクトリを再帰的に探索しません。

日本語難しい(´・ω・)
少し簡単な説明

require の部分は ディレクティブ (何種類かあります)
require_directory
→与えられたディレクトリ以下のファイルを、
自身よりも前に挿入する。
順番はアルファベット順(さらに大文字→小文字)になる

require_tree
→require=directory と同じ動きをするが、再帰的に読み込む
読み込みはコードの 上から順番に 読み込まれます。

上記より考えると、require_treeより下に書かれていたjqueryが読み込まれなかったため動かなかった(と、解釈いたしました。)

[備考]
require_treeには引数として与えられたディレクトリ以下のファイルをアルファベット順に全て読み込むという意味があります。
現在require_treeの引数には.(ドット)が渡されています。
引数.(ドット)はカレントディレクトリを表します。
つまり、この記述によってapp/assets/javascriptsというディレクトリにあるファイルは全て読み込まれることになります。

参考ページ

Rails のアセットパイプライン(Asset Pipeline)について

RailsでCSSの読み込む順番を制御する方法

終わりに

こちらは、個人的解釈をもとに解決した方法を備忘録として書いております。
プログラミング初学者ゆえ、
誤記や不備、アドバイス等ございましたら御指摘いただけると幸いです。
最後まで読んでいただきありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ルーティングをネストした時にshallowオプションを使うと便利だよ

ネストしたルーティングにshallowオプションを使うと便利だよ!と知ったので、自分なりに記述してみます?

ルーティングをネストする

  • このような親子関係のモデルがあるとする。
    • Boardモデルがhas_many :comments
    • Commentモデルがbelongs_to :board

以下の記述は、ルーティングをネストすることで、掲示板(Board)とコメント(Comment)の親子関係をルーティングで表しています。

config/routes.rb
resources :boards do
  resources :comments
end

次にターミナルでrails routesを実行し、利用可能なルーティングをすべて表示してみます。

  • rails routesで確認できる情報
    • ルーティング名 (あれば)
    • 使用されているHTTP動詞 (そのルーティングがすべてのHTTP動詞に応答するのでない場合)
    • マッチするURLパターン
    • そのルーティングで使うパラメータ
# rails routes
    board_comments GET    /boards/:board_id/comments(.:format)              comments#index
                   POST   /boards/:board_id/comments(.:format)              comments#create
 new_board_comment GET    /boards/:board_id/comments/new(.:format)          comments#new
edit_board_comment GET    /boards/:board_id/comments/:id/edit(.:format)     comments#edit
     board_comment GET    /boards/:board_id/comments/:id(.:format)          comments#show
                   PATCH  /boards/:board_id/comments/:id(.:format)          comments#update
                   PUT    /boards/:board_id/comments/:id(.:format)          comments#update
                   DELETE /boards/:board_id/comments/:id(.:format)          comments#destroy
            boards GET    /boards(.:format)                                 boards#index
                   POST   /boards(.:format)                                 boards#create
         new_board GET    /boards/new(.:format)                             boards#new
        edit_board GET    /boards/:id/edit(.:format)                        boards#edit
             board GET    /boards/:id(.:format)                             boards#show
                   PATCH  /boards/:id(.:format)                             boards#update
                   PUT    /boards/:id(.:format)                             boards#update
                   DELETE /boards/:id(.:format)                             boards#destroy

この状態だと、ある掲示板(board)のコメント(comments)の詳細ページをeditしたり、showしたりする時のURLは、
/boards/:board_id/comments/:id/edit
/boards/:board_id/comments/:id
といった風に指定する必要があり、冗長ですね。

shallowオプションの登場

そこで、ネストしたルーティングに、以下の様にshallow: trueを付けてあげます。

config/routes.rb
resources :boards do
  resources :comments, shallow: true
end
# rails routes
    board_comments GET    /boards/:board_id/comments(.:format)           comments#index
                   POST   /boards/:board_id/comments(.:format)           comments#create
 new_board_comment GET    /boards/:board_id/comments/new(.:format)       comments#new
      edit_comment GET    /comments/:id/edit(.:format)                   comments#edit
           comment GET    /comments/:id(.:format)                        comments#show
                   PATCH  /comments/:id(.:format)                        comments#update
                   PUT    /comments/:id(.:format)                        comments#update
                   DELETE /comments/:id(.:format)                        comments#destroy
            boards GET    /boards(.:format)                              boards#index
                   POST   /boards(.:format)                              boards#create
         new_board GET    /boards/new(.:format)                          boards#new
        edit_board GET    /boards/:id/edit(.:format)                     boards#edit
             board GET    /boards/:id(.:format)                          boards#show
                   PATCH  /boards/:id(.:format)                          boards#update
                   PUT    /boards/:id(.:format)                          boards#update
                   DELETE /boards/:id(.:format)                          boards#destroy

すると、index, new, createの3つのアクション(Commentのidを指定しないアクション)はBoardのidを必要としますが、それ以外のアクションは子のidを指定するだけで済むようになりました。
これは、

  • index、new、createは、Commentのidを指定しないため、Boardのidを指定しないと、どのBoardに対するものか参照できない。
  • show、edit、update、destroyは、Commentのidを指定するため、それだけで一意性を持てるから、Boardのidを指定する必要がない。

と考えると分かりやすいかなと思います!

参照先

Rails のルーティング(https://railsguides.jp/routing.html)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Perl による next_prime の実装

はじめに

アルゴリズムの勉強に、もっとプログラマ脳を鍛える数学パズル アルゴリズムが脳にしみ込む70問 を解いています。
『Q53 重さが素数の荷物を運ぶエレベーター』で素数の話が出てきました。
(Q53 は、素数+二分探索の良問です)

Ruby の 素数クラス

Ruby では標準で素数を順に抽出するクラス Prime が用意されています。

ruby.rb
require 'prime'

primes = Prime.each(100).to_a # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

上記のコーディングで、100以下の素数の配列が生成されます。
Perl では標準モジュールが用意されていないので、Rubyに乗り換えましょう自作する必要があります。

Perl による実装

primes.pl
my @primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97);
sub prime {
  my $n = shift;
  return $primes[$n] if defined $primes[$n];
  my $newprime = $primes[-1] + 2;
  while ($n + 1 > @primes) {
    my $f = 1;
    for my $i (@primes) {
      if ($newprime % $i == 0) {
        $f = 0;
        last;
      } elsif ($i ** 2 > $newprime) {
        last;
      }
    }
    if ($f == 1) {
      $primes[@primes] = $newprime;
    }
    $newprime += 2;
  }
  $primes[$n];
}
sub next_prime {
  my $n = shift;
  my $i = 0;
  while (1) {
    if ($primes[$i] == 0) {
      prime(scalar @primes);
      last if $n < $primes[$i];
    }
    $i++;
  }
  $primes[$i];
}
perl.pl
print prime(80);       # => 419 81番目の素数
print next_prime(743); # => 751

怠慢なので 予め100以下の素数を用意しておきます。
それ以降の素数は必要に応じ、随時追加する仕様にしました。

まとめ

  • Rubyprime や permutation や gcd 等が標準で用意されてます
  • 今回は Perlnext_prime を実装しました
  • 数学パズルはためになります

参照したサイト
class Prime
初学者によるプログラミングMemo #22 素因数分解

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む