20200216のRubyに関する記事は24件です。

Rails ビュー

ビューはレスポンスとして見た目を返すものになります。
コントローラーの次にビューが実行される。
app/views/コントローラー名ディレクトリにアクション名.html.erbというファイル名で作成される。
a.png

コントローラにインスタンス変数を定義

#app/controllers/posts_controller.rbに定義する場合

class PostsController < ApplicationController
  def index
    @post = "これはコントローラーで定義したインスタンス変数を確認するための文字列です"
  end
end

#ビューファイル(app/views/posts/index.html.erb)にインスタンス変数を定義

<h1>トップページ</h1>
<%= @post %>

これで以下のようになる。
a.png

なぜできる?

a.png

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

Rails コントローラーついて

ルーティングを設定したらコントローラーを設定します。
コントローラーはルーティングで振り分けられたリクエストを実際に処理する役割を持っています。
a.png

コントローラーの作成

$ rails g controller コントローラー名

postsコントローラーを作成する場合

$ rails g controller posts

#app/controllers/posts_controller.rbが作成されているか確認
コントローラー内にアクションを記載する
class PostsController < ApplicationController

end

#作ったばっかりでは何も記載はない

indexアクションを定義する場合↓

class PostsController < ApplicationController

  def index  # indexアクションを定義した
  end

end 

これでコントローラーの大まかな作業は完了です。続きはビュー編でお伝えします。

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

Rails ルーティング設定について

ルーティングとはどの要求を受けたら、どのコントローラを呼び出すかを決めている場所。

ルーティングの記載

【例】config/routes.rbに記載
以下はHTTPメソッドとリクエスト、そして行き先のコントローラーとアクションが明示されている形です。

HTTPメソッド
クライアントから送られたリクエスト(GET[ページを表示する操作]、POST[データを登録する操作]、PUT[データを変更する操作]、DELETE[データを削除する操作])のこと。
URIパターン

URLのようなもの。例えば、http://localhost:3000/posts
にアクセスする際はpostsのみを記載。

コントローラー

ルーティングの次にやってくる処理のこと。

アクション

コントローラー内における、処理のカテゴリの事。
ルーティングは「どのようなリクエスト」=>「どのようなコントローラー・アクション」をイメージ。
Rails.application.routes.draw do
  [HTTPメソッド] '[URIパターン]', to: '[コントローラー名]#[アクション名]'
end

#例として以下のように記載
Rails.application.routes.draw do
  root to: 'posts#index'
  get 'posts', to: 'posts#index'
  get 'posts/new', to: 'posts#new'
  post 'posts', to: 'posts#create'
end

#get 'posts', to: 'posts#index'を例にとる。
#リクエスト: GETのHTTPメソッド、URLはhttp://localhost:3000/posts
#行き先: postsコントローラーという名前のコントローラー、indexアクションという名前のアクション
resources メソッドを使用したバージョン

resourcesを使用すると7つのアクション(index[一覧表示ページを表示]、new[新規投稿ページを表示]、create[データの投稿を行う]、show[個別詳細ページを表示]、edit[投稿編集ページを表示]、update[データの編集を行う]、destroy[データの削除])全てを実装したことになる。

onlyオプション

指定したアクションのルーティングのみを設定できる。

Rails.application.routes.draw do
  root to: 'posts#index'
  resources :posts, only: [:index, :new, :create]
end

rails routesコマンド

ルーティングの確認ができる。
a.png

Prefixとは

a.png

Prefixを指定した場合はパスの書き方が変わります。詳しくはビューファイル設定の際に説明します。

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

Rails アプリ作成に向けての準備

実際にアプリケーションを作成するにあたっての説明です。

rails new コマンド

Railsで新規アプリケションを作成する際に使用します。

$ rails new アプリケーション名 -オプション名
# オプションを付けてアプリケーションを作成

例:sample_appleを作成する場合

# Railsのバージョン5.2.3を用いて、「sample_apple」を「-d」オプションでMySQLを指定して作成。
$ rails _5.2.3_ new sample_apple -d mysql

# 「sample_apple」ディレクトリに移動
$ cd sample_apple

# 現在のディレクトリのパスを表示
$ pwd

データベースを作成

a.png

rails db:create
を実行して新しくデータベースを作成します。
$ rails db:create  # データベースの作成
Created database 'sample_apple_development'
Created database 'sample_apple_test'

これで開発環境とテスト環境用のデータベースが作成完了。
確認はdatabase.ymlで確認できます。

データベースはSequel Proで管理する前提で説明します。
以下の設定でデータベース接続。

a.png

rails s

開発者のみがローカルでサーバーを立ち上げるためのコマンドです。
control + cでサーバーを切ることができる。

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

rubocopをbundlerでインストールするときにrequire: falseにする理由

本記事の対象者

「rubocopを導入するときにrequire: falseにしてたけど、理由がよく分からない」

そんな方に向けてこの記事を書いています。

rubocopの導入方法

rubocopの公式ドキュメントでは、以下のコマンドを実行する

$ gem install rubocop`

もしくは、Gemfileに以下を記載して、bundlerで導入してくださいと書いてあります。

$ gem 'rubocop', require: false`

この記事では、後者について記載していきます。

bundlerの特徴

rubocopを導入するうえで、覚えておきたいbundlerの特徴は、
「bundlerでは、Gemfileに書いたgemをまとめて自動でrequireする仕組みになっている」ということです。

rubocopはどこで使うの?

rubocopは、ソースコードが規約に沿っているか確認するために、ターミナル等でコマンドを実行します。

結論

rubocopは、ターミナル等で使用するため、bundlerによってアプリ側に自動で読み込む必要がない。よって、require: falseにする。

参考

主に以下を参考にさせていただきました。ありがとうございます!

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

Gemfileでインストールするgemを間違えた時の対処法

本記事の対象者

「インストールしようと思っていたgemを間違えてしまった」
「Gemfileでインストールしたgemを削除したい」
そんな方に向けてこの記事を書いています。

ステップ1:Gemfileから削除する

はじめに、Gemfileから間違ってインストールしたgemの記載を削除します。

ステップ2:Gemfile.lockから削除する

次に、Gemfile.lockの記載を更新します。Gemfile.lockは手動で修正せずに、以下のコマンドを実行して、自動で修正するようにしてください。

$ bundle install

もしくは

$ bundle install --path vendor/bundle

まとめ

Gemfileでインストールするgemを間違えた際には、以上のたった2ステップを実行することで対処できます。

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

Gemfileでインストールするgemを間違えた時の取り消し方

本記事の対象者

「インストールしようと思っていたgemを間違えてしまった」
「Gemfileでインストールしたgemを削除したい」
そんな方に向けてこの記事を書いています。

ステップ1:間違ってインストールしたgemを削除する

まずは、以下のコマンドを実行して、間違ってインストールしたgemを削除しましょう。このとき削除するgemによっては、他のgemやコードに影響を及ぼす可能性があるので、注意してください。

$ bundle exec gem uninstall <gem名>

ステップ2:Gemfileから削除する

続いて、Gemfileから間違ってインストールしたgemの記載を削除します。

ステップ3:Gemfile.lockから削除する

最後に、Gemfile.lockの記載を更新します。Gemfile.lockは手動で修正せずに、以下のコマンドを実行して、自動で修正するようにしてください。

$ bundle install

もしくは

$ bundle install --path vendor/bundle

まとめ

間違ってインストールしたgemは、①gemのアンインストール、②Gemfileの書き換え、③Gemfile.lockの書き換えの3ステップで行えました。
ただし、gemをアンインストールする前に、他のgemやコードへの影響がないか確認をしてから行うようにしましょう!

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

DXRuby:「当たり判定」を自分で作ってみよう A. 四角形の四隅の座標で判定する

概要

この記事は中学高校生向けプログラミング教室の教材として作ったものを一部改変したものです。

今回の記事は、
DXRuby:「当たり判定」を自分で作ってみよう - Qiita

● 当たり(衝突)判定の方法
A. 四角形の四隅の座標で判定する
B. 円の中心からの距離で判定する
C. 色で判定する

の内の
A. 四角形の四隅の座標で判定する
になります。

atari_7b.png

次の記事
DXRuby:「当たり判定」を自分で作ってみよう B. 円の中心からの距離で判定する - Qiita

技術解説

  • 使用ライブラリ
  • 参考サイト については、上記記事を参照してください。

ライセンス

ソースコード、本解説ともにパブリックドメイン

プログラム解説

A. 四角形の四隅の座標で判定する

→ 「ブロック崩し」追加課題 1a);衝突判定の自作(四角) - noanoa 日々の日記
http://blog.livedoor.jp/noanoa07/archives/2046179.html

A-1a. ===を使った準備:判定相手が単独(atari_1.rb)

DXRubyのスプライトでは、===を使うと、衝突の有/無でtrue/fasleを返します。

ball(小さい正方形)が、block(大きい長方形)と衝突しているかを===で判定します。
衝突/非衝突 の結果をターミナル(コマンドプロンプト)にtrue/falseで出力しつつ、blockの色も/にします。
また、ウィンドウに衝突時はhit!の文字列を表示します。

atari_1.rb
require 'dxruby'

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball  = Sprite.new(300, 400, image0)
block = Sprite.new(200, 200, image1)

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  result = (ball === block)
  p result

  if result
    Window.draw_font(0, 0, "hit!", font)
    block.image = image2
  else
    block.image = image1
  end

  block.draw
end

atari_1a.png

atari_1b.png

A-1b. atari?を自作:判定相手が単独(atari_2.rb)

atari?(jibun, aite)を作って、 衝突の有/無でtrue/fasleを返すようにします。

判定方法は、互いの四角形の四隅の座標を比較することにします。

  • 自分jibunx座標x0xx0、y座標はy0yy0

  • 相手aitex座標xxx、y座標はyyy

2つが重なっている条件は;

  • aitex座標の範囲xxx の中に、jibunx0 または xx0 があること

かつ

  • aitey座標の範囲yyy の中に、jibuny0 または yy0 があること

これをコーディングすると以下のようになります。

&&かつ||またはの意味)

(x <= x0  && x0  <= xx  ||
 x <= xx0 && xx0 <= xx)

 &&

(y <= y0  && y0  <= yy  ||
 y <= yy0 && yy0 <= yy)

また、スプライトの画像の高さを求めるには、

スプライト.image.width

高さスプライト.image.height

を使います。

→ DXRubyリファレンス:API INDEX;Sprite;image
http://mirichi.github.io/dxruby-doc/api/Sprite_23image.html

→ DXRubyリファレンス:API INDEX;Image;width,height
http://mirichi.github.io/dxruby-doc/api/Image.html

それでは、自作したatari?を使って、===を使ったコードを書き換えてみます。

atari_2.rb
require 'dxruby'

def atari?(jibun, aite)
  x0  = jibun.x
  xx0 = x0 + jibun.image.width

  y0  = jibun.y
  yy0 = y0 + jibun.image.height

  x   = aite.x
  xx  = x  + aite.image.width

  y   = aite.y
  yy  = y  + aite.image.height

  (x <= x0  && x0  <= xx ||
   x <= xx0 && xx0 <= xx) &&

  (y <= y0  && y0  <= yy ||
   y <= yy0 && yy0 <= yy)
end

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball  = Sprite.new(300, 400, image0)
block = Sprite.new(200, 200, image1)

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  result = atari?(ball, block)
  p result

  if result
    Window.draw_font(0, 0, "hit!", font)
    block.image = image2
  else
    block.image = image1
  end

  block.draw
end

atari_2a.png

atari_2b.png

A-2a. ===を使った準備:判定相手が配列(atari_3.rb)

DXRubyのスプライトでは、===は、相手がスプライトの配列でも、衝突の有/無でtrue/fasleを返します。

ballが、スプライトの配列blocksと衝突しているかを===で判定します。
blocksのいずれかと衝突していたら、ターミナル(コマンドプロンプト)にtrueで出力し、それ以外はfalseを出力します。
また、ウィンドウに衝突時はhit!の文字列を表示します。

atari_3.rb
require 'dxruby'

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball   = Sprite.new(300, 400, image0)

block1 = Sprite.new( 10, 200, image1)
block2 = Sprite.new(250, 200, image1)

blocks = [block1, block2]

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  result = (ball === blocks)
  p result

  if result
    Window.draw_font(0, 0, "hit!", font)
  end

  Sprite.draw(blocks)
end

atari_3a.png

atari_3b.png

atari_3c.png

atari_3d.png

A-2b. atari_array?を自作:判定相手が配列(atari_4.rb)

===の代わりに、atari_array?(jibun, array)を自作します。
判定相手の配列の要素を一つずつatari?で判定していきます。

自作したatari_array?を使って、コードを書き換えます。

atari_4.rb
require 'dxruby'

def atari?(jibun, aite)
  x0  = jibun.x
  xx0 = x0 + jibun.image.width

  y0  = jibun.y
  yy0 = y0 + jibun.image.height

  x   = aite.x
  xx  = x  + aite.image.width

  y   = aite.y
  yy  = y  + aite.image.height

  (x <= x0  && x0  <= xx ||
   x <= xx0 && xx0 <= xx) &&

  (y <= y0  && y0  <= yy ||
   y <= yy0 && yy0 <= yy)
end

def atari_array?(jibun, array)
  array.each do |a|
    if atari?(jibun, a)
      return true
    end
  end
  false
end

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball   = Sprite.new(300, 400, image0)

block1 = Sprite.new( 10, 200, image1)
block2 = Sprite.new(250, 200, image1)

blocks = [block1, block2]

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  result = atari_array?(ball, blocks)
  p result

  if result
    Window.draw_font(0, 0, "hit!", font)
  end

  Sprite.draw(blocks)
end

atari_4a.png

atari_4b.png

atari_4c.png

atari_4d.png

A-3a. checkを使った準備(atari_5.rb)

DXRubyの===や、自作したatari_array?では、どの相手に衝突したかは分かりませんでした。

一方、DXRubyのスプライトではcheckを使うと、衝突している相手すべてを配列に入れて返します。(衝突していない時は、何も入ってない配列を返す)

この配列をarrayとすると、0番目のarray.firstarray[0]でも同じ)が最初に衝突した相手です。また、array.firstに何か入っていれば衝突している、空(nil)ならば衝突していないという、当たり判定にも使えます。

→ DXRubyリファレンス:Spriteを使うためのチュートリアル;衝突したオブジェクトを取得する
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html

→ DXRubyリファレンス:API INDEX;Sprite;check
http://mirichi.github.io/dxruby-doc/api/Sprite_23check.html

プログラムでは、ballblocksのどれに最初に衝突しているかを check.firstで判定します。
最初に衝突しているブロックをターミナル(コマンドプロンプト)に出力し、そのブロックを赤色にします。それ以外はnilを出力します。
また、ウィンドウに衝突時はhit!の文字列を表示します。

atari_5.rb
require 'dxruby'

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball   = Sprite.new(300, 400, image0)

block1 = Sprite.new( 10, 200, image1)
block2 = Sprite.new(250, 200, image1)

blocks = [block1, block2]

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  col = ball.check(blocks).first
  p col

  if col
    Window.draw_font(0, 0, "hit!", font)
    col.image = image2
    Sprite.draw(blocks)
    col.image = image1
  else
    Sprite.draw(blocks)
  end
end

atari_5a.png

atari_5b.png

atari_5c.png

atari_5d.png

A-3b. atari_arrayを自作(atari_6.rb)

ぶつかった相手は最初のものだけ分かればよいので、DXRubyのcheck.first相当のatari_arrayを自作します。

つまり、atari_arrayは、衝突していないとnilを返し、衝突すると "ぶつかった最初のもの" を返します。

方法は、判定相手の配列の要素を一つずつatari?で判定していき、最初に衝突している要素を返します。

自作したatari_arrayを使って、コードを書き換えます。

atari_6.rb
require 'dxruby'

def atari?(jibun, aite)
  x0  = jibun.x
  xx0 = x0 + jibun.image.width

  y0  = jibun.y
  yy0 = y0 + jibun.image.height

  x   = aite.x
  xx  = x  + aite.image.width

  y   = aite.y
  yy  = y  + aite.image.height

  (x <= x0  && x0  <= xx ||
   x <= xx0 && xx0 <= xx) &&

  (y <= y0  && y0  <= yy ||
   y <= yy0 && yy0 <= yy)
end

def atari_array(jibun, array)
  array.each do |a|
    if atari?(jibun, a)
      return a
    end
  end
  nil
end

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball   = Sprite.new(300, 400, image0)

block1 = Sprite.new( 10, 200, image1)
block2 = Sprite.new(250, 200, image1)

blocks = [block1, block2]

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  col = atari_array(ball, blocks)
  p col

  if col
    Window.draw_font(0, 0, "hit!", font)
    col.image = image2
    Sprite.draw(blocks)
    col.image = image1
  else
    Sprite.draw(blocks)
  end
end

atari_6a.png

atari_6b.png

atari_6c.png

atari_6d.png""

A-4. 自作の当たり判定(四角)をSpriteクラスのメソッドにする(atari_7.rb)

元の===checkは、Spriteクラスのメソッド(命令)なので、書き方としては、ball.check(blocks)のようになります。

自作のatari_arrayも同じような書き方になるように、Spriteクラスのメソッドにしてみます。

クラスメソッドについての説明はここでは説明を省きますので、詳しくはRubyのテキストを見てください。

クラスメソッドを追加する方法は簡単で、以下のようにします。

class Sprite
  def 追加したいメソッド
    #追加したいメソッドの内容
  end
end

Spriteクラスにatari?atari_array追加します。

ちなみに、jibun(自分)に相当するものは、selfと書きます。

class Sprite
  def atari?(aite)
    x0  = self.x
    xx0 = x0 + self.image.width

    y0  = self.y
    yy0 = y0 + self.image.height

    x   = aite.x
    xx  = x  + aite.image.width

    y   = aite.y
    yy  = y  + aite.image.height

    (x <= x0  && x0  <= xx ||
     x <= xx0 && xx0 <= xx) &&

    (y <= y0  && y0  <= yy ||
     y <= yy0 && yy0 <= yy)
  end

  def atari_array(array)
    array.each do |a|
      if self.atari?(a)
        return a
      end
    end
    nil
  end
end

これで、クラスメソッドになったので、今までのatari_array(ball, blocks)という書き方ではなく、ball.atari_array(blocks)というように書くことができるようになりました。

atari_7.rb
require 'dxruby'

class Sprite
  def atari?(aite)
    x0  = self.x
    xx0 = x0 + self.image.width

    y0  = self.y
    yy0 = y0 + self.image.height

    x   = aite.x
    xx  = x  + aite.image.width

    y   = aite.y
    yy  = y  + aite.image.height

    (x <= x0  && x0  <= xx ||
     x <= xx0 && xx0 <= xx) &&

    (y <= y0  && y0  <= yy ||
     y <= yy0 && yy0 <= yy)
  end

  def atari_array(array)
    array.each do |a|
      if self.atari?(a)
        return a
      end
    end
    nil
  end
end

image0 = Image.new( 50,  50, C_WHITE)
image1 = Image.new(200, 100, C_WHITE)
image2 = Image.new(200, 100, C_RED)

ball   = Sprite.new(300, 400, image0)

block1 = Sprite.new( 10, 200, image1)
block2 = Sprite.new(250, 200, image1)

blocks = [block1, block2]

font = Font.new(32)

Window.loop do
  ball.x = Input.mouse_pos_x
  ball.y = Input.mouse_pos_y

  ball.draw

  col = ball.atari_array(blocks)
  p col

  if col
    Window.draw_font(0, 0, "hit!", font)
    col.image = image2
    Sprite.draw(blocks)
    col.image = image1
  else
    Sprite.draw(blocks)
  end
end

atari_7a.png

atari_7b.png

atari_7c.png

atari_7d.png

A-5. 自作の当たり判定(四角)を使ったブロック崩し(atari_8.rb)

「ブロック崩し」のプログラムblock28.rbを、自作のatari_arrayに書き換えてみましょう。

元の===check.firstを同じ動きをしているのがわかると思います。

atari_8.rb
require 'dxruby'

class Sprite
  def atari?(aite)
    x0  = self.x
    xx0 = x0 + self.image.width

    y0  = self.y
    yy0 = y0 + self.image.height

    x   = aite.x
    xx  = x  + aite.image.width

    y   = aite.y
    yy  = y  + aite.image.height

    (x <= x0  && x0  <= xx ||
     x <= xx0 && xx0 <= xx) &&

    (y <= y0  && y0  <= yy ||
     y <= yy0 && yy0 <= yy)
  end

  def atari_array(array)
    array.each do |a|
      if self.atari?(a)
        return a
      end
    end
    nil
  end
end

img_bar   = Image.new(100,  20, C_WHITE)
img_hwall = Image.new( 20, 480, C_BLUE)
img_vwall = Image.new(640,  20, C_BLUE)
img_ball  = Image.new( 20,  20).circle_fill(10, 10, 10, C_RED)
img_block = Image.new( 58,  18, C_GREEN)
img_block_y = Image.new( 58,  18, C_YELLOW)

bar   = Sprite.new(  0, 460, img_bar)
lwall = Sprite.new(  0,   0, img_hwall)
rwall = Sprite.new(620,   0, img_hwall)
twall = Sprite.new(  0,   0, img_vwall)

walls = [bar, lwall, rwall, twall]

ball  =  Sprite.new(300, 400, img_ball)
dx =  2
dy = -2

def move(sprite, speed_x, speed_y)
  sprite.x += speed_x
  sprite.y += speed_y
end

blocks = []
10.times do |x|
  5.times do |y|
    blocks << Sprite.new(21 + 60 * x, 21 + 20 * y, img_block)
  end
end


Window.loop do
  bar.x = Input.mouse_pos_x
  Sprite.draw(walls)

  move(ball, dx, 0)
  if ball.atari_array(walls)
    ball.x -= dx
    dx = -dx
  end
  coll_x = ball.atari_array(blocks)
  if coll_x
    coll_x.image = img_block_y
    coll_x.draw     #一瞬色が変わって表示
    coll_x.vanish   #消える
    ball.x -= dx
    dx = -dx
  end

  move(ball, 0, dy)
  if ball.atari_array(walls)
    ball. y -= dy
    dy = -dy
  end
  coll_y = ball.atari_array(blocks)
  if coll_y
    coll_y.image = img_block_y
    coll_y.draw     #一瞬色が変わって表示
    coll_y.vanish   #消える
    ball. y -= dy
    dy = -dy
  end

  ball.draw

  Sprite.draw(blocks)
end

atari_8.png

参考

プログラミング初心者向け:DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita
で作った「ブロック崩し」のプログラムです。

  • block28.rb
block28.rb
require 'dxruby'

img_bar   = Image.new(100,  20, C_WHITE)
img_hwall = Image.new( 20, 480, C_BLUE)
img_vwall = Image.new(640,  20, C_BLUE)
img_ball  = Image.new( 20,  20).circle_fill(10, 10, 10, C_RED)
img_block = Image.new( 58,  18, C_GREEN)
img_block_y = Image.new( 58,  18, C_YELLOW)

bar   = Sprite.new(  0, 460, img_bar)
lwall = Sprite.new(  0,   0, img_hwall)
rwall = Sprite.new(620,   0, img_hwall)
twall = Sprite.new(  0,   0, img_vwall)

walls = [bar, lwall, rwall, twall]

ball  =  Sprite.new(300, 400, img_ball)
dx =  2
dy = -2

def move(sprite, speed_x, speed_y)
  sprite.x += speed_x
  sprite.y += speed_y
end

blocks = []
10.times do |x|
  5.times do |y|
    blocks << Sprite.new(21 + 60 * x, 21 + 20 * y, img_block)
  end
end


Window.loop do
  bar.x = Input.mouse_pos_x
  Sprite.draw(walls)

  move(ball, dx, 0)
  if ball === walls
    ball.x -= dx
    dx = -dx
  end
  coll_x = ball.check(blocks)
  if coll_x[0]
    coll_x[0].image = img_block_y
    coll_x[0].draw     #一瞬色が変わって表示
    coll_x[0].vanish   #消える
    ball.x -= dx
    dx = -dx
  end

  move(ball, 0, dy)
  if ball === walls
    ball. y -= dy
    dy = -dy
  end
  coll_y = ball.check(blocks)
  if coll_y[0]
    coll_y[0].image = img_block_y
    coll_y[0].draw     #一瞬色が変わって表示
    coll_y[0].vanish   #消える
    ball. y -= dy
    dy = -dy
  end

  ball.draw

  Sprite.draw(blocks)
end

block28.png

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

SIerの僕、プログラミング始めてみた(10月スタート・初投稿)

簡単な自己紹介

SIerに2年間在籍しております、かなたです。
誰も興味がない趣味とか仕事とか、本記事のきかっけを初めに。

  • 好き①:服(Margielaとか)
  • 好き②:映画(おすすめは Into the Wild)
  • 業務①:DataSpiderを用いたデータ連携基盤を構築
  • 業務②:C#デスクトップ系業務アプリ

10月からWEBアプリケーションの勉強を独学で始めまして、よくよくエラー等ひっかかります。
昨日の自分自身に対して、教えるならどのように伝えるかを、ここに表現できればと思っています。
(あとモチベーションアップに繋がれば…という思いです)

作っているもの

SIer(以外もそうだと思うんですが)は基本的に、実績ベースで予定・工数を積み上げ、スケジューリングします。
ですが、実績を記録しているPJ/チームを僕は見たことがありません。
そのため、予実データ化し改善までつながるアプリケーションを開発しよう!と始めてみました。

成果

さてさて、10月からおよそ4ヶ月の成果はざっくり以下の通りです。

プログラミング基本

  • HTML/CSS/JavaScript/Ruby/Railsの基本(Progate)
  • Railsチュートリアル2周(GitHub~Herokuデプロイ)
  • Vueサンプル複写(基礎から学ぶ Vue.js)

  • リーダブルコード
  • 初めてのRuby
  • Webを支える技術
  • Web API: The Good Parts
  • テスト駆動開発
  • リファクタリング
  • エンジニアファースト
  • エンジニアの知的生産術
  • コンピュータはなぜ動くのか
  • デザイン系の本を数冊

アプリケーションの実装状況

  • フロントエンド ・・・ Vue.js + Vuex + VueRouter + Vuetify
  • サーバサイド ・・・ RubyonRails
  • 実装:メールアドレスを利用したユーザの登録〜ログインおよびユーザ情報の更新
  • 詳細:ユーザログイン時には、WebStorageを利用。ログイン永続化を実装。

ざっくりこんな状況です。頑張らないと・・・

さいごに

また、目標・スケジュールをこちらに記載できればと思っています(もしかして、そういった場でない?)
そして、次回からは、冒頭で述べたとおり、自分自身の備忘録として、
学んだことを表現させていただきます。

よろしくお願いいたします。

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

DXRuby:「当たり判定」を自分で作ってみよう

概要

この記事は中学高校生向けプログラミング教室の教材として作ったものを一部改変したものです。

ゲームで大変よく使う「当たり判定」(衝突判定)について、DXRubyでは===checkなどの便利な機能が付いています。しかし、いつもこのような機能が使えるとは限りません。

では、自分で作るとしたらどうしたらよいでしょうか?実は、いろいろな判定方法が考えられます。

atari_7b.png

atari_en7b.png

前の記事
プログラミング初心者向け:DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita

次の記事
DXRuby:「当たり判定」を自分で作ってみよう A. 四角形の四隅の座標で判定する - Qiita

当たり(衝突)判定の方法

いろいろなやり方が考えられるでしょうが、まずは以下の3通りのやり方が思いつきます。

A. 四角形の四隅の座標で判定する
B. 円の中心からの距離で判定する
C. 色で判定する

それぞれ、別記事になっています。

技術解説

使用ライブラリ

Windows向けRuby用2DゲームライブラリDXRubyを使用します。
バージョン1.4.2以上を想定しています。

1.4.2より前のバージョンとの主な相違点

  • Window.loopが複数置ける

  • マウス位置を取得するInput.mouse_pos_xInput.mouse_pos_yの新しい書き方として、Input.mouse_xInput.mouse_yが追加

→ DXRuby 1.4.6:更新履歴

http://mirichi.github.io/dxruby-doc/CHANGELOG.html

DXRubyインストールの注意点

→ DXRuby 1.4.6 をWindows10で使う時の注意点とインストール方法 - noanoa07 - Qiita
https://qiita.com/noanoa07/items/0ce14c2404df38de94b7

参考サイト

困ったときは、このページの「チュートリアル」と「マニュアル」にだいたい書いてあります。

古いバージョンのリファレンスも役に立つときがあります。

このテキストのブログ記事での解説です。

ライセンス

ソースコード、本解説ともにパブリックドメイン

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

メソッド内にループがある場合の戻り値(返り値)について

皆さんこんにちは! プログラマーを目指して学習している、久保雄貴と申します。

今回は「メソッド内にループがある場合の戻り値(返り値)について」自分が疑問に思ったこと、考えたこと、知ったことについて書いてみようと思います。

あるときrubyの勉強中にエラーが起きました。メソッドの戻り値を使って演算をしようとした時です。
「undefined method '+' for nil:nilclass」 のような表示がされました。
空のオブジェクトと足し算はできません。ということのようです。
頭の中に「?」がたくさん出てきて、自分が戻り値についてよく理解できていないことがわかりました。
そこでいろいろ考え、実験して、調べてみたわけです。

皆さんのお役に立てる内容であれば嬉しいです。

なお、間違っている点や「こういう風に書いたらいいよ」などアドバイスがありましたら、コメントをください。

では始めましょう!

1. 戻り値とは

戻り値とは、オブジェクトやメソッドが処理された後の最終的な値のことです。返り値とも言います。
Rubyのオブジェクト自体、またはメソッドを利用した「式」には、全て戻り値があります。

Rubyにおける「式」とは文字列や数値の他に、変数やメソッド呼び出し、演算子式などが含まれます。
すなわち、"あいうえお", 5296, (4 + 24)などはすべて式になります。
そしてそれらすべてに戻り値があるということです。(上の例でいうと、"あいうえお", 5296, 28)

2. メソッドの戻り値はどうなる?

では、メソッドの戻り値はどうなるのでしょうか。
Ruby Referenceで調べてみました。

http://ruby-for-beginners.rubymonstas.org/writing_methods/return_values.html

以下のように書かれています。

In Ruby, a method always return exactly one single thing (an object).
Also note that in Ruby we do not have to use the statement return, as in other languages. In fact, most Ruby code does not use the keyword return at all.

This is extremely convenient, but it is also something we need to learn:

If we don’t do anything else, then a method will return the value that was returned from the last evaluated statement. Most often, this is the last line in the method body.

端的に言えば、
「メソッドは一つの値を返が、他の言語と違って、returnと記述する必要はない。
もし記述しなかったら、最後の式の値が返される。」
という感じでしょうか。

例えば、

def test(number)
  number + 1      ←メソッドの中の最後の式
end

puts test(5)

これを実行すると、6と表示されますね。
"number + 1" の答えである "6" がtestメソッドの戻り値となっているわけです。

3. 実験: メソッドの戻り値の判定

冒頭で書きましたが、メソッドの戻り値を使って演算をしようとした時に、「戻り値は「nil」ですよ。」とエラーが出ました。実は、その時はメソッド内にwhile文(ループ)がありました。
条件を満たすまで、命令を実行し、条件を満たしたら、ループから抜け出して、メソッドの呼び出し元にもどる。
その戻り値で演算をしようとしてエラーが出たわけです。

そこでメソッドの戻り値が「nil」かそうでないかを判断してみたらどうかと思いつきました。

メソッドの戻り値が「nil」なのかそうでないのかを判断するコードを考えましたので、みていきたいと思います。
まずはwhile文なしのパターンです。

def test(count)
    puts "0より大きい数字を入力してください。"
    count = gets.to_i
end

count = test(count)

if count.nil?
  puts "test(count)の戻り値はnilです。"
else
  puts "test(count)の戻り値は#{count}です。"
end

「.nil?」はcount(レシーバ)の中身が「nil」の場合trueを返し、「nilでない」場合falseを返すメソッドです
例えば、ターミナルで実行して、24という数字を入れたとすると、

代替テキスト

と表示されます。

上のコードでは予想通り、returnの記述がなくても戻り値は「nil」ではありませんでした。


では次はどうでしょう。
testメソッドの中にwhile文を入れて、10より大きい数字が入るまで、入力させ続けます。
そして、その戻り値が「nil」かどうかを判断します。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
  end

end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

ターミナルで実行してみましょう。
今回は、「4→24」の順で入れてみます。(while文が機能しているか判断するため。)

代替テキスト

なんと今回は戻り値が「nil」になりました!




次は、while文の後に「return count」を入れてみます。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
  end
  return count   ←追加しました
end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

実行してみましょう。

代替テキスト

当然のことながら、戻り値は「24」ですね。

4. 結果

さて、もう一度まとめてみましょう。
メソッドの戻り値が「nil」かどうかを判断しました。

順番 条件 結果
while文なし 戻り値: 24
2 while文あり かつ returnメソッドなし 戻り値: nil
3 while文あり かつ returnメソッドあり 戻り値: 24

5. 考察

さて、上記の結果になりました。
ここからは、あくまで自分で考えた内容です。(信憑性はとても低いです)

Rubyは、メソッド内において式から得られる「値」をどこかに仮置きしているのではないか。
例えば、1の実験では

def test(count)
    puts "0より大きい数字を入力してください。"
    count = gets.to_i      ←ここで入力された値をどこかに仮置きしている。
end

count = test(count)

if count.nil?
  puts "test(count)の戻り値はnilです。"
else
  puts "test(count)の戻り値は#{count}です。"
end

1) 3行目で「値」をどこかに仮置きする。
2) もしメソッドの中に次の式があれば、仮置きしている「値」を破棄し、その次の「値」をまた仮置きする。(今回はなし)
3) メソッドが終わったら、一番最後に仮置きした「値」をメソッドの戻り値として返す。

と行った感じです。

ただ、メソッドの中にループ文がある場合は、ループから抜け出す時になぜかその仮置きしておいた「値」を破棄してしまうのではないか。
なぜかはわかりません。。。(考察になっていない?)

6. 結論

実は、このブログを書く前にいろいろと参考になるものはないかと探していたところ、答えになるものを見つけました。
Rubyのリファレンスマニュアルです。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fcontrol.html

この中のwhileに関する説明の一番下にこう書かれています。

代替テキスト

while は nil を返します。

はっきり書かれてますね(笑)

なぜそういうふうになっているのかは書いてありませんが、そうなっているもんは仕方ない。
今回はそう割り切りましょう。

ということで、
結論は、「Rubyのルールでそうなっているから」です。



7. 感想

なんか予想外な感じになってしまいました。
でも、まあ、すっきりできたかな?




さて、気を取り直して、
今後学習を進めていくにあたって、事情がない限り、returnメソッドを書いておこうと思いました。
実は、"return count" と書かずに直接 "count" と書いても同じ結果になります。
でもそれだとその記述が何をしているのかわかりません。

ループの有無に関わらず、他のプログラマーのためにも、コードを見やすくしたり、そのメソッドが何をしているのかがわかりやすいように書いた方が良さそうです。



いかがだったでしょうか。
今回二回目の投稿ですが、学んだことは、
1) 疑問に思ったことは、自分で仮説を立てて、実験して、結果から自分なりの答えを出すことがとても大事ということ。
2) その過程は実は楽しいこと。
3) その過程の中でいろいろと調べるので、途中で答えが見つかることもあるということ。
4) ブログという形でアウトプットすることは思った以上に緊張するし怖い部分があるが予想以上に学びがあること
です。

いいねをもらえたり、ストックしていただけるのもとても励みになります。
ありがとうございます。m(_ _)m

今回は以上です。ありがとうございました。




おまけ

リファレンスマニュアルに

また、引数を伴った break により while 式の戻り値をその値にすることもできます。

と書かれていたので調べてみました。

https://hydrocul.github.io/wiki/programming_languages_diff/control_flow/break.html#ruby

breakの後ろに数字あるいは変数を書くといいみたいです。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
    break count    ←ここです
  end

end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

やってみましょう。

代替テキスト

「nil」になりませんでした!



もうお気づきかもしれませんが、これには問題が。。。

代替テキスト

そうです。ループされないんです。
ループの意味なし。
なんじゃこりゃ。。。。

ということで 本当におしまいです。ありがとうございました。



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

Spreeで使われているfactorybot

spreeで使われているfactorybot。
勉強のために切り抜きました。参考までに。

product_factory.rb
FactoryBot.define do
  factory :base_product, class: Spree::Product do
    sequence(:name)   { |n| "Product ##{n} - #{Kernel.rand(9999)}" }
    description       { generate(:random_description) }
    price             { 19.99 }
    cost_price        { 17.00 }
    sku               { generate(:sku) }
    available_on      { 1.year.ago }
    deleted_at        { nil }
    shipping_category { |r| Spree::ShippingCategory.first || r.association(:shipping_category) }

    # ensure stock item will be created for this products master
    before(:create) { create(:stock_location) unless Spree::StockLocation.any? }

    factory :custom_product do
      name  { 'Custom Product' }
      price { 17.99 }

      tax_category { |r| Spree::TaxCategory.first || r.association(:tax_category) }
    end

    factory :product do
      tax_category { |r| Spree::TaxCategory.first || r.association(:tax_category) }

      factory :product_in_stock do
        after :create do |product|
          product.master.stock_items.first.adjust_count_on_hand(10)
        end
      end

      factory :product_with_option_types do
        after(:create) { |product| create(:product_option_type, product: product) }
      end
    end
  end
end

taxon_factory.rb
FactoryBot.define do
  factory :taxon, class: Spree::Taxon do
    sequence(:name) { |n| "taxon_#{n}" }
    association(:taxonomy, strategy: :create)
    parent_id { taxonomy.root.id }
  end
end
taxonomy_factory.rb
FactoryBot.define do
  factory :taxonomy, class: Spree::Taxonomy do
    sequence(:name) { |n| "taxonomy_#{n}" }
  end
end

詳細は本家githubのファイルで。
https://github.com/spree/spree/tree/master/core/lib/spree/testing_support/factories

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

RailsでJavaScriptでの遷移時にフラッシュを表示させる

やりたいこと

RailsでJavaScriptで遷移させるときに、フラッシュを表示させたい。
調べるとフラッシュに相当する文言を隠して( display: none とか?) おいて、JavaScriptで表示させる( display: block とか)の方法しかなかった。
GETパラメータで送るのヤダなぁと思ったので別の方法を考えた。

やったこと

アクションを作ってフラッシュ付きでリダイレクトさせる

具体的にはこう。

items.js
location.href = '/items/redirect_with_flash_message';
items_controller.rb
class ItemsController < WebController
  def redirect_with_flash_message
    flash[:alert] = 'ログインが必要です'

    redirect_to new_user_session_path
  end
end

どうでもいいけどRailsのタグ、スペル間違ってない?

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

CarrierWaveでデフォルト画像の設定

CarrierWaveでユーザーアイコンの実装をしていたのですが、デフォルト画像の表示で少し躓いてしまったのでメモしておきます。

前提

  • CarrierWaveは導入済み
  • 登録した画像表示はできる

image_uploader.rbの設定

CarrierWaveを入れたときに

$ rails g uploader image

をしたらimage_uploader.rbというファイルが生成されます。

このファイルを以下のようにいじってください。

image_uploader.rb
#アップロードした画像の表示
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

#デフォルト画像の設定
  # Provide a default URL as a default if there hasn't been a file uploaded:
  def default_url(*args)
  #   For Rails 3.1+ asset pipeline compatibility:
    ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  #   "/images/fallback/" + [version_name, "default.png"].compact.join('_')
  end

これで登録時にファイル選択をしなかった場合、デフォルト画像をimageとして渡す準備ができました。

assets/images配下に画像を設置

assets/images配下にデフォルト画像を設置してください。
画像名はこの場合「default.png」にしときましょう。

Viewの設定

Viewに、if文でユーザーのimageがある場合とない場合で表示が変わるように設定してあげましょう。

以下は例です。

_show.html.erb
<% if post.user.image? %>
  <img src='<%= post.user.image %>' class="icon" alt="ユーザーアイコン">
<% else %>
  <image src="/assets/default.png" class="icon" alt="ユーザーアイコン" %>
<% end %>

まとめ

CarrierWaveさんは非常に便利なのですが、デフォルト画像設置に関しては画像パスとして渡っているので、dbを調べてもnullとしか表示されず焦ってしまうというパターンがよくあるようです。

よく使う機能だとは思いますので、誰かの助けになれば幸いです。

ではでは。

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

ツイッターデベロッパーでのapp作成手順

前回の記事で、TwitterAPIの利用申請が完了しました。

実際にRubyでbotを作成するには、デベロッパー上でappを作成する必要がありますので、本記事ではその手順を記載します。

手順1 Appの名称と説明を入力する

作成するAppの名称をApp name欄に入力してください。
(すでに登録されている名称は使用できません。)

Application description欄には、アプリの説明を記載してください。簡単にで問題ありません。
Image from Gyazo

手順2 必須のURLを入力する

Website URL、Callback URLを入力してください。
本来は本番環境でのURLを入力する欄ですが、とりあえず登録段階ではキータの自分のページや、ツイッターのトップページ等でも問題ありません。
開発段階では、
http://localhost:3000/
と記載したいところですが、2020年2月時点では認められませんでした。
※この部分については、キータでも記事によって記入方法が異なると思います。
しかし、登録の段階では何でも良いので入るものを入れてしまいましょう。
Image from Gyazo

手順3 Appの使用方法について説明する

開発予定のアプリケーションがどのように使用されるのか記載しましょう。
簡単にで問題ありません。

記入が完了したら、Createボタンを押してください。
その後出てくるポップアップ画面でも、Createボタンを押してください。
Image from Gyazo

Image from Gyazo

手順4 登録内容を確認する

以下のような画面が表示されたら登録完了です。
内容を変更したい場合は、右上のEditボタンから編集してください。
Image from Gyazo

以上で登録作業は完了です。
次の記事では、登録したAppを使用して、実際にRubyファイルからツイートする方法を解説します。

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

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

RubyでルールのあるSortプログラムを作る

はじめに

rubyでオリジナルのソートを作るためのメモ
JSONが入っているのは自分のローカルサーバで作りやすかったから

コード

ruby.rb
require 'json'

$jsonfile = <<EOF
[{"id":643,"name":"7_to_7_3","tag":"ミットナイトレストラン7to703,胡桃ちの,漫画","created_at":"2018-12-02T11:30:34.013Z","updated_at":"2018-12-02T11:30:34.013Z"},
{"id":644,"name":"7_to_7_4","tag":"ミットナイトレストラン7to704,胡桃ちの,漫画","created_at":"2018-12-02T11:31:05.979Z","updated_at":"2018-12-02T11:31:05.979Z"},
{"id":641,"name":"7_to_7_10","tag":"ミットナイトレストラン7to710,胡桃ちの,漫画","created_at":"2018-11-29T12:01:43.992Z","updated_at":"2018-11-29T12:01:43.992Z"},
{"id":645,"name":"7_to_7_11","tag":"ミットナイトレストラン7to711,胡桃ちの,漫画","created_at":"2018-12-02T11:31:47.163Z","updated_at":"2018-12-02T11:31:47.163Z"}]
EOF

def num_kaiseki(str)
    output = []
    tmp = str
    num = str.rindex(/[0-9]/)
    out =""
    count = 0
    if (num !=nil)
        while(tmp[num-count])
            if (tmp[num-count]=="_")
            elsif (tmp[num-count].index(/[0-9]/)==nil)
                if(out[0]==".")
                    out = out[1..tmp.size]
                end
                break
            end
            if (tmp[num-count]=="_")
                out = "."+out
            else
                out = tmp[num-count]+out
            end
            count+=1
        end
    else
        num = tmp.size
    end
    output = [tmp[0..tmp.size-out.size-1] ,out.to_f]
end
def sort_jouken(a,b)
    t_a = num_kaiseki(a)
    t_b = num_kaiseki(b)
    if (t_a[0] == t_b[0])
        output = t_a[1]<=>t_b[1]
    else
        output = t_a[0]<=>t_b[0]
    end
    output
end

list = JSON.parse($jsonfile)
list.each do |word|
    p num_kaiseki(word["name"])
end
bb = list.sort { |a,b| sort_jouken(a["name"],b["name"]) }
bb.each do|word|
    print word["id"],":",word["name"],","
end

不満点

小数点のルール条件めちゃくちゃだから、どうにかしないと

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

FontAwesomeの導入

FontAwesomeとは?

ウェブフォントの一種。
文字を扱うのと同じようにアイコンを表示させることができる。

導入方法

FontAwesome用のGemがをインストールする。
全ての環境で使用したいので、最下部に以下のコードを記載する。

Gemfile
gem 'font-awesome-sass'

bundle installとサーバ再起動を忘れずに。

assetsフォルダに書き込み

application.scss
@import "font-awesome-sprockets";
@import "font-awesome";

補足

CSSの仕様で、paddingなどの設定を行った場合、その要素全体のサイズが
大きくなる場合がある。以下の記述を追加すると、paddingやborderの設定を行っても、
要素の大きさがwidthやheightで指定したサイズが維持される。

messeges.scss
* {
  box-sizing: border-box;
}

「*」は、全ての要素に適用させるという意味

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

rails 基礎講義資料2章 Scaffold を用いた高速なアプリケーション構築 及び MVCの理解

1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用

1.Scaffold

この章では、Railsの強力な機能をいくつか紹介するためのアプリケーションを作成します。
大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って蔵書管理アプリケーションを生成し、
それを元にRailsの概要を一緒にみていきましょう。

*3章以降では、scaffold は使わずに、自力で作る方法を学びます。

scaffold コマンド

まずは、名前と学籍番号を登録できるだけの機能を作成します。
下記のコマンドは一字一句間違いなくタイプするか、コピペで行ってください。

失敗した場合は、下記のページに解決策が記載してあります。(すこし複雑です)
scaffold作成時にカラム名(属性名)を打ち間違えた場合の修正手順

terminal
$bin/rails generate scaffold User name:string number:integer

Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。scaffoldコマンドの引数には、リソース名を単数形にしたもの (この場合はUser) を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します

リソースとは、コントローラが扱う対象に名前をつけたものです。
例:モデル、画像、セッション...etc

自動で多数のファイルが生成されたとおもいます。
その中で重要なのは次のファイルです。

terminal
    create    app/models/user.rb
    create    app/views/users/(略)
    create    app/controllers/users_controller.rb
    route     resources :users

ここで生成された Models Views Controllers の頭文字をとって
MVC と呼びます。MVCについては後ほど、説明します。

データベースの利用

このアプリケーションではデータを保存するために、データベースを扱います。
下記コマンドを実行することで、データベースに users という名前のテーブルが作成されます。

terminal
$bin/rails db:migrate

下記のような結果が表示されていればテーブルの生成ができています。
(migrate については今後の章で解説します)

terminal
==  CreateUsers: migrating =============================
-- create_table(:users)
   -> 0.0007s
==  CreateUsers: migrated (0.007s) =============================

コマンド実行後に、結果の文章を読む癖もつけていきましょう
初学者が陥る問題に、terminalにエラー文が表示されているにも関わらず気付かない問題があります。
エラー文を見つけた場合は、エラー文をそのまま検索することで解決策を見つけることも難しくありません。
コマンド実行の結果、どういったことが行われているのかを見ることも理解を深めることに役立ちます。

いま実行した2行で下記の機能が自動的に生成されています。
・ユーザーの作成
・ユーザーの一覧表示
・ユーザーの編集
・ユーザーの削除

試しにサーバーを起動してみましょう。

terminal
$bin/rails server

ブラウザで開いてみましょう。
下記URLにアクセスすると。それに対応した機能が呼び出されます。
一覧表示
 http://localhost:3000/users
新規作成
 http://localhost:3000/users/new
ユーザー表示
 http://localhost:3000/users/1
ユーザー編集
 http://localhost:3000/users/1/edit

たった2行で想像以上の機能が実装されてしまい驚きと困惑があると思います。
多くの初学者が、何が起きたのかわからず Rails を魔法のように感じてしまいます。

自動で作られたファイルについて、簡略的な説明を行っていきます。
3章以降で、いちからつくるので事前に概要を知っておくことは、
今後の理解を深める上で重要です。

ルーティング

まずはどの機能が、どのURLと紐付いているかですが、
下記のコマンドで確認できます。

terminal
$bin/rails routes

下記のような表示がなされていると思います。

GET    /users(.:format)           users#index
POST   /users(.:format)           users#create
GET    /users/new(.:format)       users#new
GET    /users/:id/edit(.:format)  users#edit
GET    /users/:id(.:format)       users#show
PUT    /users/:id(.:format)       users#update
DELETE /users/:id(.:format)       users#destroy

よくみると、show update destroy がすべて /users/:id という同じURLになっています。
同じURLでも機能が異なるのは、利用しているHTTPメソッドが異なるためです。

show : GET データの取得
update : PUTデータの送信(主に更新の際)
destroy : DELETE:データの削除
 HTTPメソッドについて、もっと知りたい方はこちらをご覧ください
 https://tsuyopon.xyz/2019/01/31/understand-4-http-methods/

この紐付け(ルーティング)は、scaffold によって自動的に行われたものですが、
自力で行う場合には config/routes.rb の中に結びつけを書いていきます。
いまは 紐付けは routes.rb で行われている。 とだけ覚えてもらえば大丈夫です。
3章以降で詳しく説明していきます。

紐付けと言われても、すこし伝わりづらいとおもいます。
ルーティングの見方を、例をあげて説明します。
/users に対するルーティングをみると
GET /users(.:format)      users#index
と書かれています。これは usersコントローラーの index アクションを呼び出す
と理解してください。

コントローラー (app/controllers/users_controller.rb) を見てみると下記のコードが書かれています。

users_controller.rb
  def index
    @users = User.all
  end

 Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味です。

 開かれる htmlファイルはどこにあるのでしょう?
 
 実は Rails では、特に指定をしない場合、
 アクション名.html.erb が自動的に呼び出されます。
 重要なので覚えておいてください。
 app/views/users/index.html.erb が呼びされます。

ここまでの流れをまとめると、下記のようになります。

 ①URLにアクセスがあった場合
 ②ルーティングに従ってコントローラーが呼び出され
 ③モデルを経由してデータを受け取り、コントローラーにデータを返し
 ④コントローラーのアクションに従って、データがビューに渡され
 ⑤ビューは受け取ったデータを利用して、HTMLを生成する

上記は、Rails を理解する上で重要な流れなので、覚えておいてください。

MVC(Model View Controller)については後ほど詳しく説明しますが、
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う
と、大まかにご理解ください。

理屈だけより実際に触れてみると理解が深まるとおもうので、
一連のながれを体感するために、View と Controller を触っていきましょう。

2.View

ページの編集

ブラウザから一覧ページを開いてみましょう
http://localhost:3000/users

まずは、メンバー一覧表示ページを編集してみましょう。
編集するファイルは、app/views/users/index.html.erb です。
3行目 h1タグ内の、 Users の部分を下記のように置き換えてみましょう。

index.html.erb
<h1>ユーザーの一覧</h1>

編集が完了したら、上書き保存し、ブラウザを更新してください。
編集が反映されていれば成功です。

erb

ERBとは、Embedded RuBy の略であり、Rubyを埋め込めるHTMLファイルのようなものと
いまは思っておいて頂いて結構です。

ERBは、HTMLテンプレートエンジンと呼ばれ、他にも haml slim などあるので
興味がある方は、調べてみてください。

Ruby の埋め込みも体験してみましょう。
erb 内に下記のコードを記述することで、その中にrubyを埋め込むことができます。

<% %> この中にRubyコードを記述できる。
<%= %> この中に書かれた Ruby は文字列として出力される。

erb:変数の利用

index.html.erb の1行目に下記の2行を追加してください。

index.html.erb
<% name = "Player" %>
<%= "ようこそ #{name} さん" %>

Ruby と同様に、変数の代入と展開が行われ
ようこそ Playerさん と表示されます。

Timeクラスの利用

下記のように追加すると、今日の日付が表示されます。

index.html.erb
<% t = Time.now %>
<%= "いまの時刻は #{t} です" %></br>
<%= "今日は #{t.month}#{t.day} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1)} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1).month}#{t.weeks_ago(1).day} 日です" %>

Timeクラスについて詳しく

【課題1】 erbの変更

5分ほど使って index.html.erb を自由に変更してみてください。
変更例)日本語化
1, name,number をそれぞれ、名前,学籍番号 に変更
2, New user を、新規ユーザー作成 に変更
3, Show,Edit,Destoroy を 表示、編集、削除 に変更
4, Ruby のコードをerbの中に書いてみる...etb

つづいてコントローラーをみていきましょう。

3.Controller

Controller から View への、データの受け渡しを確認

Controllerは、クライアント Model View の仲介を担っています。
Controller名は、複数形にする(命名規則)

再度、コントローラー (app/controllers/users_controller.rb) の下記の行をみてください。

users_controller.rb
  def index
    @users = User.all
  end

 これは、Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味でした。

今回はデータベースを使わずに、データを作成してみましょう。
(このあと Model の項目で、データベースを利用するように置き換えます)

@book という連想配列を作成し、その中に情報を書き込んでいきます。

users_controller.rb
  def index
    @users = User.all
    @book = Hash.new
    @book[:country] = "Japan"
    @book[:title] = "吾輩は猫である"
    @book[:author] = "夏目漱石"
    @book[:year] = 1905
    @book[:book_id] = 1
    @book[:comment] = "おもしろかった"
  end

@book[:title] Hash(連想配列)のSymbolです。お忘れの方は
Array(配列)とHash(連想配列)入門
を御覧ください。

本の情報を @book 連想配列に追加したので、 View で受け取れるか確認してみましょう。
(def index に記載しているので、 index.html.erb で受け取れます)

index.html.erb
<%= @book %></br>
<%= "本のタイトルは #{@book[:title]} です" %></br>
<%= "著者は #{@book[:author]} です" %></br>

http://localhost:3000/users
ブラウザを更新すると下記のように表示されます。

 {:country=>"Japan", :title=>"吾輩は猫である", :author=>"夏目漱石", :year=>1905,
  :book_id=>1, :comment=>"おもしろかった"}
 本のタイトルは 吾輩は猫である です
 著者は 夏目漱石 です

無事、情報を受け取れていますね。

erbの応用

ここに和書かどうかを調べる機能を追加してみましょう。
erb はRubyが使えるので、条件分岐も利用できます

下記のコードを追加してみましょう。

index.html.erb
<% if @book[:country] == "Japan" %>
<p>この本は、和書です</p>
<% end %>

 この本は、和書です
と表示されます。

すこし蔵書管理アプリっぽくなりました。

蔵書データを増やしていきたいところですが、問題があります。
Controller に書くには蔵書データはあまりに多すぎるという問題です。
(そしてユーザーは追記できない)

これを解決するのが、データベースとModelです。

@book に関するコードは、すべて消してしまいましょう。

4.Model

Books リソースの作成

Modelは基本的に、1つのテーブルに対して1つのModelを作成します。
Model名は、複数形にする(命名規則)

人に関するリソースは作成したので、本に関するリソースも作成していきましょう。
(usersテーブルとは、別にbooksテーブルを作成するので、Book Modelを作成します)

再度、Scaffold を実行します。

terminal
$bin/rails generate scaffold Book country:text title:text author:text year:integer user_id:integer comment:text

Users リソースの時と同様に、自動で多くのファイルが作成されます。
Model View Controller route もそれぞれ作成されています。

terminal
    create    app/models/book.rb
    create    app/views/books/(略)
    create    app/controllers/books_controller.rb
    route     resources :books

前回同様、下記コマンドを実行することで、データベースに books という名前のテーブルが作成されます。

terminal
$bin/rails db:migrate

ルーティングも確認してみましょう。

terminal
$bin/rails routes

下記のように、紐付けされていることが確認できます。

GET    /books(.:format)           books#index
POST   /books(.:format)           books#create
GET    /books/new(.:format)       books#new
GET    /books/:id/edit(.:format)  books#edit
GET    /books/:id(.:format)       books#show
PUT    /books/:id(.:format)       books#update
DELETE /books/:id(.:format)       books#destroy

ブラウザで確認してみましょう。本の情報を入力できるようになっています。
http://localhost:3000/users/new

このままではユーザーは自由に投稿できる状態です。
year と user_id は数値を入力してもらいたいので入力制限をかけてみましょう。

Rails では制限(validation)も簡単に設定することができます。

Validation

app/models/book.rb を開き下記のように編集してください。

book.rb
class Book < ApplicationRecord
  validates :year, :user_id, numericality: true
end

この1行だけで制限を設けることができます。
Book Model に追記しているので、 books テーブルの yearカラム と user_idカラムに対して制限をかけます。
実際に試してみると、下記のようなエラー文が表示されます。
validate.png

【課題2】自作バリデーション

Railsバリデーションまとめ
を参考にして、制限を設けてみましょう。
例)
1, Title を空白禁止にする
2, 著者の名前を125文字以内にする ...etc

バリデーションがうまくかけたかを試すために、
実際にブラウザから本のデータを2件登録してみてください。

内容は自由で良いですが、 user の項目だけは 1 にしてください

rails console

うまく情報が登録されたかを確認するために、データベースから情報を取り出してみましょう。
SQL文が苦手な方もご安心ください。
Rails console を使えば、簡単にデータを取り出してみることができます。

新規ターミナルを起動し、下記のコマンドを実行してください。

terminal
  $bin/rails c

すると >> といった表示がされるとおもいます。これでrails console が起動しました。
( exit 入力し、Enterを実行すると終了します )

試しに、最初に登録したユーザー情報を、(Model を経由し)データベースから取り出してみましょう

railsConsole
  >> user = User.find(1)

user はただの変数です。(名前は自由ですが、わかりやすく use にしました)
User.find(1) は User Model を利用して、 1番目の人を取り出せ
という意味ですので、 U は大文字です。(Model は頭文字が大文字で単数形でしたね)

さきほど、ブラウザから入力したデータが登録されていると思います。

【課題3】 rails c を使った、データの取り出し

本のデータを取り出して表示してみましょう。
その際に、find(1) 意外の取り出し方も試してみましょう。
データ取得メソッドまとめ
例) .all を使用してみる。

MVC総合 User が所持しているデータを表示してみる。

さてここまでで、冒頭に説明した下記の役割を体験してみました。
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

なにもわからなかった状態から、
ルーティング・DB・コンソール・MVC
と多くのことを吸収し、成長を感じられると思います。

まだ大まかな理解だと思うので、更に理解を深めるべく
応用課題に取り組んでいきましょう!

id: 1 のUserが所持している本のデータを取り出してみましょう。

ブラウザでの表示は View を編集する必要があり大変なので
まずは console の中で、取り出しを確認するところだけみていきましょう。
(小さく着実に作っていくことは大事です)

rails console を起動した端末にて下記のコマンドを実行します。

railsConsole
   >> user = User.find(1)
   >> user.books

と入力することで、 id:1 のuserが持っている本のデータ
(booksテーブルの中で、 user_id を 1 にした本のデータ)
がすべて表示されます!

.
..
...
されないとおもいます。
実行結果を見てみると、エラー文が出ていますね

railsconsole
>> user.books
Traceback (most recent call last):
        1: from (irb):2
NoMethodError (undefined method `books' for #<User:0x00007fa63942dce0>)

NoMethodError undefined method `books'
これは、 books がなにかわからない。定義されていない。という意味のエラーです。
(今後良くみかけることになるとおもいます。タイピングミスしてもこのエラーが表示されます)

今回の原因は、 user テーブルと books テーブルが紐付いていないことによるものです。

booksテーブルで勝手に user_id というカラムを追加しただけで、
userテーブルにはなにも書いていないので当然ですね。
Userモデルから、bookテーブルのデータを取得できないのでエラーが出ています
こういったテーブル同士の紐付けも、Railsでは1行で出来ます。

has_many belongs_to

いま、 users と books の2つのテーブルがあります。
どの蔵書データが、どのユーザーに紐付いているのかといった関連付けを行いたいとき
どうすれば良いのかを学んでいきます。

1,一人のuserは、たくさんの本を持っています
こういう場合、 user.rb (UserのModel)に、
has_many :books (たくさんなので、複数形です)
と1行追加するだけで、上記のような紐付けが完了します。

2,1冊の本は、一人のUserに紐付いています
こういう場合、 book.rb (BookのModel)に、
belongs_to :user (ひとつなので、単数形です)
と追加するだけで、上記のような紐付けが完了します。

user.rb
class Book < ApplicationRecord
  has_many :books
end
book.rb
class Book < ApplicationRecord
  belongs_to :user
  validates :year, :user_id, numericality: true
end

has_many belongs_to コード追加が終われば
rails console を立ち上げ直してください
(exitとタイプしEnterで終了できます)

再度 rails c を起動し、さきほどと同じコマンドを実行するとどう変わっているでしょうか。

railsConsole
   >> user = User.find(1)
   >> user.books

↓出力結果例

railsConsole
Loading development environment
>> user = User.find(1)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "daichi", number: 10

>> user.books
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Book id: 1, country: "Japan", title: "吾輩は猫である", author: "夏目漱石", year: 1905, user_id: 1, comment: "おもしろかった"

紐付けられたことでエラーが解消され、上記のような結果が表示されます。

テーブルに関連性を持たせたいときは has_many belongs_to を使用すること
覚えておいてください。

Modelにて、データベース内のテーブルの関連づけは出来き、
user.books にてユーザーが持っている本のデータを取り出せることが確認できました。

さて、Modelで取り出したデータを、View に送るにはどうすればよかったでしょうか?
そうですね。Controller の役割でしたね。

Controller

app/controllers/users_controller.rb
をひらきます(@bookを消していない人は消しておいてください)

前回は、index.html.erb に情報を渡したかったので index アクションに
@book のデータを(データベースを使わずに直接)書きました。

今回は、show.html.erb に情報を渡したいので、 show アクションに
モデルを用いて、データを取り出すコードを書きます

app/controllers/users_controller.rb
  def show
    @user = User.find(1)
    @books = @user.books
  end

(小文字大文字, s の有無に気をつけてください)
最後に、Controller から送られてきたデータの表示の部分をやっていきます。

View

表示させたい場所は、1番目のユーザーのページなのでこちらです
http://localhost:3000/users/1

【MVCの流れの復習】
ちなみに、ルーティングには下記のように書いてあります
 GET /users/:id       users#show
users/(idの数字) にGETリクエストがあったら users controller の show アクションを実行でしたね。
show アクションが、実行されたら自動的に、 show.html.erb が表示されるのでした。

show.html.erb の一番上に下記コードを追記してください。

app/views/users/show.html.erb
  <% @books.each do |book| %>
    <p><%= "本のタイトルは #{book[:title]} です" %></p>
    <p><%= "著者は #{book[:author]} です" %></p></br>
  <% end %>

これで、 Controller から受け取った @books (本の情報が格納された連想配列)
の中身が表示されていると思います。
(配列の中身を順番に取り出していくときは each でした。 Ruby をお忘れの方は復習を)

【最終課題】

show.html.erb を自由に編集してください

例)
・本の情報をすべて表示

まとめ

以上で、scaffold を用いた、高速蔵書アプリケーションの制作講座は終わりです。
お疲れ様でした。

3章では、scaffold が自動生成していた部分を、1から自分で制作していきます。
今回の内容を再度読み返すか、もう一度素早く作成して復習をしておいてください。

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

Before Rails Tutorial2章 Scaffold を用いた高速なアプリケーション構築 及び MVCの理解

本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用

1.Scaffold

この章では、Railsの強力な機能をいくつか紹介するためのアプリケーションを作成します。
大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って蔵書管理アプリケーションを生成し、それを元にRailsの概要を一緒にみていきましょう。

*3章以降では、scaffold は使わずに、自力で作る方法を学びます。

scaffold コマンド

まずは、名前と学籍番号を登録できるだけの機能を作成します。
下記のコマンドは一字一句間違いなくタイプするか、コピペで行ってください。

失敗した場合は、下記のページに解決策が記載してあります。(すこし複雑です)
scaffold作成時にカラム名(属性名)を打ち間違えた場合の修正手順

terminal
$bin/rails generate scaffold User name:string number:integer

Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。scaffoldコマンドの引数には、リソース名を単数形にしたもの (この場合はUser) を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します

リソースとは、コントローラが扱う対象に名前をつけたものです。
例:モデル、画像、セッション...etc

自動で多数のファイルが生成されたとおもいます。
その中で重要なのは次のファイルです。

terminal
    create    app/models/user.rb
    create    app/views/users/(略)
    create    app/controllers/users_controller.rb
    route     resources :users

ここで生成された Models Views Controllers の頭文字をとって
MVC と呼びます。MVCについては後ほど、説明します。

データベースの利用

このアプリケーションではデータを保存するために、データベースを扱います。
下記コマンドを実行することで、データベースに users という名前のテーブルが作成されます。

terminal
$bin/rails db:migrate

下記のような結果が表示されていればテーブルの生成ができています。
(migrate については今後の章で解説します)

terminal
==  CreateUsers: migrating =============================
-- create_table(:users)
   -> 0.0007s
==  CreateUsers: migrated (0.007s) =============================

コマンド実行後に、結果の文章を読む癖もつけていきましょう
初学者が陥る問題に、terminalにエラー文が表示されているにも関わらず気付かない問題があります。
エラー文を見つけた場合は、エラー文をそのまま検索することで解決策を見つけることも難しくありません。
コマンド実行の結果、どういったことが行われているのかを見ることも理解を深めることに役立ちます。

いま実行した2行で下記の機能が自動的に生成されています。
・ユーザーの作成
・ユーザーの一覧表示
・ユーザーの編集
・ユーザーの削除

試しにサーバーを起動してみましょう。

terminal
$bin/rails server

ブラウザで開いてみましょう。
下記URLにアクセスすると。それに対応した機能が呼び出されます。
一覧表示
 http://localhost:3000/users
新規作成
 http://localhost:3000/users/new
ユーザー表示
 http://localhost:3000/users/1
ユーザー編集
 http://localhost:3000/users/1/edit

たった2行で想像以上の機能が実装されてしまい驚きと困惑があると思います。
多くの初学者が、何が起きたのかわからず Rails を魔法のように感じてしまいます。

自動で作られたファイルについて、簡略的な説明を行っていきます。
3章以降で、いちからつくるので事前に概要を知っておくことは、
今後の理解を深める上で重要です。

ルーティング

まずはどの機能が、どのURLと紐付いているかですが、
下記のコマンドで確認できます。

terminal
$bin/rails routes

下記のような表示がなされていると思います。

GET    /users(.:format)           users#index
POST   /users(.:format)           users#create
GET    /users/new(.:format)       users#new
GET    /users/:id/edit(.:format)  users#edit
GET    /users/:id(.:format)       users#show
PUT    /users/:id(.:format)       users#update
DELETE /users/:id(.:format)       users#destroy

よくみると、show update destroy がすべて /users/:id という同じURLになっています。
同じURLでも機能が異なるのは、利用しているHTTPメソッドが異なるためです。

show : GET データの取得
update : PUTデータの送信(主に更新の際)
destroy : DELETE:データの削除
 HTTPメソッドについて、もっと知りたい方はこちらをご覧ください
 https://tsuyopon.xyz/2019/01/31/understand-4-http-methods/

この紐付け(ルーティング)は、scaffold によって自動的に行われたものですが、
自力で行う場合には config/routes.rb の中に結びつけを書いていきます。
いまは 紐付けは routes.rb で行われている。 とだけ覚えてもらえば大丈夫です。
3章以降で詳しく説明していきます。

紐付けと言われても、すこし伝わりづらいとおもいます。
ルーティングの見方を、例をあげて説明します。
/users に対するルーティングをみると
GET /users(.:format)      users#index
と書かれています。これは usersコントローラーの index アクションを呼び出す
と理解してください。

コントローラー (app/controllers/users_controller.rb) を見てみると下記のコードが書かれています。

users_controller.rb
  def index
    @users = User.all
  end

 Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味です。

 開かれる htmlファイルはどこにあるのでしょう?
 
 実は Rails では、特に指定をしない場合、
 アクション名.html.erb が自動的に呼び出されます。
 重要なので覚えておいてください。
 app/views/users/index.html.erb が呼びされます。

ここまでの流れをまとめると、下記のようになります。

 ①URLにアクセスがあった場合
 ②ルーティングに従ってコントローラーが呼び出され
 ③モデルを経由してデータを受け取り、コントローラーにデータを返し
 ④コントローラーのアクションに従って、データがビューに渡され
 ⑤ビューは受け取ったデータを利用して、HTMLを生成する

上記は、Rails を理解する上で重要な流れなので、覚えておいてください。

MVC(Model View Controller)については後ほど詳しく説明しますが、
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う
と、大まかにご理解ください。

理屈だけより実際に触れてみると理解が深まるとおもうので、
一連のながれを体感するために、View と Controller を触っていきましょう。

2.View

ページの編集

ブラウザから一覧ページを開いてみましょう
http://localhost:3000/users

まずは、メンバー一覧表示ページを編集してみましょう。
編集するファイルは、app/views/users/index.html.erb です。
3行目 h1タグ内の、 Users の部分を下記のように置き換えてみましょう。

index.html.erb
<h1>ユーザーの一覧</h1>

編集が完了したら、上書き保存し、ブラウザを更新してください。
編集が反映されていれば成功です。

erb

ERBとは、Embedded RuBy の略であり、Rubyを埋め込めるHTMLファイルのようなものと
いまは思っておいて頂いて結構です。

ERBは、HTMLテンプレートエンジンと呼ばれ、他にも haml slim などあるので
興味がある方は、調べてみてください。

Ruby の埋め込みも体験してみましょう。
erb 内に下記のコードを記述することで、その中にrubyを埋め込むことができます。

<% %> この中にRubyコードを記述できる。
<%= %> この中に書かれた Ruby は文字列として出力される。

erb:変数の利用

index.html.erb の1行目に下記の2行を追加してください。

index.html.erb
<% name = "Player" %>
<%= "ようこそ #{name} さん" %>

Ruby と同様に、変数の代入と展開が行われ
ようこそ Playerさん と表示されます。

Timeクラスの利用

下記のように追加すると、今日の日付が表示されます。

index.html.erb
<% t = Time.now %>
<%= "いまの時刻は #{t} です" %></br>
<%= "今日は #{t.month}#{t.day} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1)} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1).month}#{t.weeks_ago(1).day} 日です" %>

Timeクラスについて詳しく

【課題1】 erbの変更

5分ほど使って index.html.erb を自由に変更してみてください。
変更例)日本語化
1, name,number をそれぞれ、名前,学籍番号 に変更
2, New user を、新規ユーザー作成 に変更
3, Show,Edit,Destoroy を 表示、編集、削除 に変更
4, Ruby のコードをerbの中に書いてみる...etb

つづいてコントローラーをみていきましょう。

3.Controller

Controller から View への、データの受け渡しを確認

Controllerは、クライアント Model View の仲介を担っています。
Controller名は、複数形にする(命名規則)

再度、コントローラー (app/controllers/users_controller.rb) の下記の行をみてください。

users_controller.rb
  def index
    @users = User.all
  end

 これは、Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味でした。

今回はデータベースを使わずに、データを作成してみましょう。
(このあと Model の項目で、データベースを利用するように置き換えます)

@book という連想配列を作成し、その中に情報を書き込んでいきます。

users_controller.rb
  def index
    @users = User.all
    @book = Hash.new
    @book[:country] = "Japan"
    @book[:title] = "吾輩は猫である"
    @book[:author] = "夏目漱石"
    @book[:year] = 1905
    @book[:book_id] = 1
    @book[:comment] = "おもしろかった"
  end

@book[:title] Hash(連想配列)のSymbolです。お忘れの方は
Array(配列)とHash(連想配列)入門
を御覧ください。

本の情報を @book 連想配列に追加したので、 View で受け取れるか確認してみましょう。
(def index に記載しているので、 index.html.erb で受け取れます)

index.html.erb
<%= @book %></br>
<%= "本のタイトルは #{@book[:title]} です" %></br>
<%= "著者は #{@book[:author]} です" %></br>

http://localhost:3000/users
ブラウザを更新すると下記のように表示されます。

 {:country=>"Japan", :title=>"吾輩は猫である", :author=>"夏目漱石", :year=>1905,
  :book_id=>1, :comment=>"おもしろかった"}
 本のタイトルは 吾輩は猫である です
 著者は 夏目漱石 です

無事、情報を受け取れていますね。

erbの応用

ここに和書かどうかを調べる機能を追加してみましょう。
erb はRubyが使えるので、条件分岐も利用できます

下記のコードを追加してみましょう。

index.html.erb
<% if @book[:country] == "Japan" %>
<p>この本は、和書です</p>
<% end %>

 この本は、和書です
と表示されます。

すこし蔵書管理アプリっぽくなりました。

蔵書データを増やしていきたいところですが、問題があります。
Controller に書くには蔵書データはあまりに多すぎるという問題です。
(そしてユーザーは追記できない)

これを解決するのが、データベースとModelです。

@book に関するコードは、すべて消してしまいましょう。

4.Model

Books リソースの作成

Modelは基本的に、1つのテーブルに対して1つのModelを作成します。
Model名は、複数形にする(命名規則)

人に関するリソースは作成したので、本に関するリソースも作成していきましょう。
(usersテーブルとは、別にbooksテーブルを作成するので、Book Modelを作成します)

再度、Scaffold を実行します。

terminal
$bin/rails generate scaffold Book country:text title:text author:text year:integer user_id:integer comment:text

Users リソースの時と同様に、自動で多くのファイルが作成されます。
Model View Controller route もそれぞれ作成されています。

terminal
    create    app/models/book.rb
    create    app/views/books/(略)
    create    app/controllers/books_controller.rb
    route     resources :books

前回同様、下記コマンドを実行することで、データベースに books という名前のテーブルが作成されます。

terminal
$bin/rails db:migrate

ルーティングも確認してみましょう。

terminal
$bin/rails routes

下記のように、紐付けされていることが確認できます。

GET    /books(.:format)           books#index
POST   /books(.:format)           books#create
GET    /books/new(.:format)       books#new
GET    /books/:id/edit(.:format)  books#edit
GET    /books/:id(.:format)       books#show
PUT    /books/:id(.:format)       books#update
DELETE /books/:id(.:format)       books#destroy

ブラウザで確認してみましょう。本の情報を入力できるようになっています。
http://localhost:3000/users/new

このままではユーザーは自由に投稿できる状態です。
year と user_id は数値を入力してもらいたいので入力制限をかけてみましょう。

Rails では制限(validation)も簡単に設定することができます。

Validation

app/models/book.rb を開き下記のように編集してください。

book.rb
class Book < ApplicationRecord
  validates :year, :user_id, numericality: true
end

この1行だけで制限を設けることができます。
Book Model に追記しているので、 books テーブルの yearカラム と user_idカラムに対して制限をかけます。
実際に試してみると、下記のようなエラー文が表示されます。
validate.png

【課題2】自作バリデーション

Railsバリデーションまとめ
を参考にして、制限を設けてみましょう。
例)
1, Title を空白禁止にする
2, 著者の名前を125文字以内にする ...etc

バリデーションがうまくかけたかを試すために、
実際にブラウザから本のデータを2件登録してみてください。

内容は自由で良いですが、 user の項目だけは 1 にしてください

rails console

うまく情報が登録されたかを確認するために、データベースから情報を取り出してみましょう。
SQL文が苦手な方もご安心ください。
Rails console を使えば、簡単にデータを取り出してみることができます。

新規ターミナルを起動し、下記のコマンドを実行してください。

terminal
  $bin/rails c

すると >> といった表示がされるとおもいます。これでrails console が起動しました。
( exit 入力し、Enterを実行すると終了します )

試しに、最初に登録したユーザー情報を、(Model を経由し)データベースから取り出してみましょう

railsConsole
  >> user = User.find(1)

user はただの変数です。(名前は自由ですが、わかりやすく use にしました)
User.find(1) は User Model を利用して、 1番目の人を取り出せ
という意味ですので、 U は大文字です。(Model は頭文字が大文字で単数形でしたね)

さきほど、ブラウザから入力したデータが登録されていると思います。

【課題3】 rails c を使った、データの取り出し

本のデータを取り出して表示してみましょう。
その際に、find(1) 意外の取り出し方も試してみましょう。
データ取得メソッドまとめ
例) .all を使用してみる。

MVC総合 User が所持しているデータを表示してみる。

さてここまでで、冒頭に説明した下記の役割を体験してみました。
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

なにもわからなかった状態から、
ルーティング・DB・コンソール・MVC
と多くのことを吸収し、成長を感じられると思います。

まだ大まかな理解だと思うので、更に理解を深めるべく
応用課題に取り組んでいきましょう!

id: 1 のUserが所持している本のデータを取り出してみましょう。

ブラウザでの表示は View を編集する必要があり大変なので
まずは console の中で、取り出しを確認するところだけみていきましょう。
(小さく着実に作っていくことは大事です)

rails console を起動した端末にて下記のコマンドを実行します。

railsConsole
   >> user = User.find(1)
   >> user.books

と入力することで、 id:1 のuserが持っている本のデータ
(booksテーブルの中で、 user_id を 1 にした本のデータ)
がすべて表示されます!

.
..
...
されないとおもいます。
実行結果を見てみると、エラー文が出ていますね

railsconsole
>> user.books
Traceback (most recent call last):
        1: from (irb):2
NoMethodError (undefined method `books' for #<User:0x00007fa63942dce0>)

NoMethodError undefined method `books'
これは、 books がなにかわからない。定義されていない。という意味のエラーです。
(今後良くみかけることになるとおもいます。タイピングミスしてもこのエラーが表示されます)

今回の原因は、 user テーブルと books テーブルが紐付いていないことによるものです。

booksテーブルで勝手に user_id というカラムを追加しただけで、
userテーブルにはなにも書いていないので当然ですね。
Userモデルから、bookテーブルのデータを取得できないのでエラーが出ています
こういったテーブル同士の紐付けも、Railsでは1行で出来ます。

has_many belongs_to

いま、 users と books の2つのテーブルがあります。
どの蔵書データが、どのユーザーに紐付いているのかといった関連付けを行いたいとき
どうすれば良いのかを学んでいきます。

1,一人のuserは、たくさんの本を持っています
こういう場合、 user.rb (UserのModel)に、
has_many :books (たくさんなので、複数形です)
と1行追加するだけで、上記のような紐付けが完了します。

2,1冊の本は、一人のUserに紐付いています
こういう場合、 book.rb (BookのModel)に、
belongs_to :user (ひとつなので、単数形です)
と追加するだけで、上記のような紐付けが完了します。

user.rb
class Book < ApplicationRecord
  has_many :books
end
book.rb
class Book < ApplicationRecord
  belongs_to :user
  validates :year, :user_id, numericality: true
end

has_many belongs_to コード追加が終われば
rails console を立ち上げ直してください
(exitとタイプしEnterで終了できます)

再度 rails c を起動し、さきほどと同じコマンドを実行するとどう変わっているでしょうか。

railsConsole
   >> user = User.find(1)
   >> user.books

↓出力結果例

railsConsole
Loading development environment
>> user = User.find(1)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "daichi", number: 10

>> user.books
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Book id: 1, country: "Japan", title: "吾輩は猫である", author: "夏目漱石", year: 1905, user_id: 1, comment: "おもしろかった"

紐付けられたことでエラーが解消され、上記のような結果が表示されます。

テーブルに関連性を持たせたいときは has_many belongs_to を使用すること
覚えておいてください。

Modelにて、データベース内のテーブルの関連づけは出来き、
user.books にてユーザーが持っている本のデータを取り出せることが確認できました。

さて、Modelで取り出したデータを、View に送るにはどうすればよかったでしょうか?
そうですね。Controller の役割でしたね。

Controller

app/controllers/users_controller.rb
をひらきます(@bookを消していない人は消しておいてください)

前回は、index.html.erb に情報を渡したかったので index アクションに
@book のデータを(データベースを使わずに直接)書きました。

今回は、show.html.erb に情報を渡したいので、 show アクションに
モデルを用いて、データを取り出すコードを書きます

app/controllers/users_controller.rb
  def show
    @user = User.find(1)
    @books = @user.books
  end

(小文字大文字, s の有無に気をつけてください)
最後に、Controller から送られてきたデータの表示の部分をやっていきます。

View

表示させたい場所は、1番目のユーザーのページなのでこちらです
http://localhost:3000/users/1

【MVCの流れの復習】
ちなみに、ルーティングには下記のように書いてあります
 GET /users/:id       users#show
users/(idの数字) にGETリクエストがあったら users controller の show アクションを実行でしたね。
show アクションが、実行されたら自動的に、 show.html.erb が表示されるのでした。

show.html.erb の一番上に下記コードを追記してください。

app/views/users/show.html.erb
  <% @books.each do |book| %>
    <p><%= "本のタイトルは #{book[:title]} です" %></p>
    <p><%= "著者は #{book[:author]} です" %></p></br>
  <% end %>

これで、 Controller から受け取った @books (本の情報が格納された連想配列)
の中身が表示されていると思います。
(配列の中身を順番に取り出していくときは each でした。 Ruby をお忘れの方は復習を)

【最終課題】

show.html.erb を自由に編集してください

例)
・本の情報をすべて表示

まとめ

以上で、scaffold を用いた、高速蔵書アプリケーションの制作講座は終わりです。
お疲れ様でした。

3章では、scaffold が自動生成していた部分を、1から自分で制作していきます。
今回の内容を再度読み返すか、もう一度素早く作成して復習をしておいてください。

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

rails 基礎講義資料3章 Scaffold を用いない開発

1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用

前章では、Scaffoldを用いて高速でアプリケーションを構築しました。
自動化された部分も多く、現段階では、大まかな理解であるとおもいます。

今回は、route Model View Controller それぞれ自分で作っていきます。
とはいっても、継承まで含めてすべて書くには理解することが膨大で大変ですので、
前回も使用した generate は使用していきます。

まずは、Railsの新しいアプリケーションを作成します。
1章:環境構築の内容を参考に作成してみましょう。

terminal
$ cd ~/rails_app
$ mkdir bukukore
$ bundle init
   (Gemfileにgem "rails","~> 5.2.3"を追加し保存してください)
$ bundle install --path vendor/bundlesa
$ bin/rails s

http://localhost:3000/ にアクセスして表示されていればokです。

$ rails s -p 3100 などとポートを変更することでサーバーを複数起動可能です。
この場合は http://localhost:3100/ にアクセスします。

MVC 復習

前章では、アプリ制作を通して、MVCの流れを理解してきました。

Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

前回、制作したながれを図を見ながら復習していきます。
それぞれの番号が、コードではどうなっていたか思い出しながら見てください。

MVC-02.png

1, ブラウザから /users へのリクエストを Rails sever に送信する
2, router によって、/users は Users controller の index アクションを呼び出す
3, index アクションから、User Model が呼び出される
4, User Model は DataBase からデータを取り出す
5, 取り出したデータを、Controller に返す
6, 受け取ったデータを @users に保存し、 View (indexアクションなので index.html.erb) に渡す。
7, ERBを実行し、@users のデータを含んだ HTMLを生成し、Controller へ返す
8, Controller は、受け取ったHTMLをブラウザにわたす

今回も Model View Controller を構築していくので、適宜図を見返してください。

Controller と View の作成

作成は基本的に generate コマンドを用います。
(gは generateコマンドの短縮形です)
g の後は、controller に続けて、コントローラーの名前(複数形)を書きます。
rails g controller Books index show new
そのうしろ(引数といいます)に書いたものは、同名のviewとアクションが自動生成されます。

tarminal
$ rails g controller Books index show new
     create    app/controllers/books_controller.rb
     route     get 'books/index'
               get 'books/show'
               get 'books/new'
     create    app/views/books
     create    app/views/books/index.html.erb
     create    app/views/books/show.html.erb
     create    app/views/books/new.html.erb

ルーティングが生成されているので、確認してみましょう。

terminal
$ rails routes
  Verb  URI             Pattern
  GET   /books/index    books#index
  GET   /books/show     books#show
  GET   /books/new      books#new

これは具体的には、 http://localhost:3000/books/index
というURLに対するリクエストがあれば、 books_controller の index アクション を呼び出すというものでした。
今回アクションの中身が空なので、Model は呼び出されず、単に対応するView (index.html.erb) が実行されるだけです。

ルーティングがうまくいってみるか確認するために、試しにアクセスしてみましょう。
http://localhost:3000/books/index

Books#index
Find me in app/views/books/index.html.erb

と表示されているとおもいます。

Viewの変更

erbの変更は前回行ったとおりですね。とてもシンプルなものでした。
Books#index の部分を、全蔵書一覧などに変更しておきましょう。

app/views/books/index.html.erb
<h1>全蔵書一覧</h1>

 erb の中身がシンプルすぎて不思議な方もいらっしゃるかもしれません。
 【気になって次にすすめない人向けの解説】
  app/views/layouts/application.html.erb を開いてください。
 コード内の、<%= yiels %> の部分に、 index.html.erb が移植されて HTML を生成しています。 
 いまはこの程度の理解で問題ありません。 https://railstutorial.jp/ でより深く理解できます。

Controllerの変更

コントローラーの中にアクションが本当に作られているかも確認してみましょう。
app/controllers/books_controller.rb を開いてみてください。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end
end

データベース内のデータを取り出すには、Controllerのアクションから
Model を呼び出せば良いのでした。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
   @book = Book.find(1)
  end

  def new
  end
end

このままではエラーがでます。(Modelも、本の情報も作っていないので)
generate コマンドを使用して、Model を作成していきます。

Model の作成

booksテーブルには、前章と同じカラムを持たせていきます。

terminal
$ bin/rails g model Book country:text title:text author:text year:integer user_id:integer comment:text
      create    db/migrate/20200202020202_create_books.rb
      create    app/models/book.rb

g の後は、model に続けて、Model名(単数形)を書きます。
データベースにテーブルを作成するには migrate を実行するのでしたね。

terminla
$ bin/rails db:migrate
== 20200202020202 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0008s
== 20200202020202 CreateBooks: migrated (0.0009s) =============================

補足. migrate はマイグレーションファイルを実行しています。
db/migrate/(作成日時)_create_books.rb というファイルです。
ひらいてみると、下記のようなコードがあると思います。

class CreateBooks < ActiveRecord
 def change
   create_table :books do |t|
    t.text :country
    t.text :title
    t.text :author
    t.integer :year
    t.integer :user_id
    t.text :comment
    t.timestamps
   end
 end
end

上記は text型であるcountryというカラム(他6つ)を持つ books テーブルを作成
せよという意味です。 change 意外に up down などもあります。詳細は下記サイトなどを参照ください
https://www.sejuku.net/blog/14229

これでbooksテーブルが作成されました。

テーブルは作成したけども、本の情報はまだなにもいれてませんね。
初期データをいれるには db/seeds.rb ファイルを使用します。
( seed = 種 という意味です )
大量のデータを入力するのは手間なのでseed.rbを作成していきます。
(そもそもブラウザからデータをいれる機能もまだ作っていません)

下記の seed ファイルを実行すると、bookテーブルに本の情報が3件保存されます

db/seeds.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

@book = Book.new
@book.country = "jp"
@book.title = "人間失格"
@book.author = "太宰治"
@book.year = 1948
@book.user_id = 2
@book.comment = "すばらしかった"
@book.save

@book = Book.new
@book.country = "gb"
@book.title = "Alice's Adventures in Wonderland"
@book.author = "Lewis Carroll"
@book.year = 1865
@book.user_id = 1
@book.comment = "Amazing"
@book.save

seed ファイルは、下記のコマンドで実行できます。

terminal
$ bundle exec rake db:seed

データが保存されたかの確認は。rails c でコンソールを起動すると確認できます。

railsConsole
@book = Book.find(1)

ブラウザ(show)で表示してみましょう。

app/view/books/show.html.erb
<p>本のタイトル: <%= @book.title %></p>
<p>著者: <%= @book.author %></p>

本のタイトル: 吾輩は猫である

著者: 夏目漱石

と表示されていればOKです。

課題1,

@book はどこで定義したか、再確認してください。

URLからパラメーターの取得

本のデータを取り出して表示するところまでうまくいきました!
すごい進歩です!

ただ、まだ不満点があります。
それは、 id:1 の本のデータしか表示できていない点です。
理由は、Contoroller の show アクションをみると、
@book = Book.find(1)
となっているためです(id:1 の本の情報しか見つけてきていない)

ここをidパラメーターが1以外も入力できるように変更するにはどうすればよいでしょうか。
@book = Book.find(params[:id])
とすることで、1以外のパラメーターを見つけてこれます。

app/controllers/books_controller.rb
 def show
    @book = Book.find(params[:id])
 end

パラメーター(数値)は、どこで指定すればよいでしょうか。
URL にパラメーターを含めて渡すテクニックがあるので覚えておきましょう。
例)ID:2の本の情報を取得したい場合 http://localhost:3000/books/show/2
  このままではルーティングエラーが表示されます。
  books/show へのリクエストに関しては記述していますが、
  books/show/(数字) へのリクエストについてはなにも書いていない為です。

config/routes.rb を開いてください。
URLに含まれる数字をパラメーターとして取得するには、下記のように記述します。

config/routes.rb
#   get 'books/show'(# でコメントアウトしてます)
get 'books/show/:id', to:'books#show'

こうすることで /books/show/2 の 2の部分が、:id パラメーターとして利用可能になります。
( 利用するときは、params[:id] とかきます)

to: 'books#show' 
books Cコントローラーの show アクションを呼び出す指示になります。

これで、任意のidの本情報を取得できるようになりました。

http://localhost:3000/books/show/1
http://localhost:3000/books/show/2

Model 検索

いま本の情報の取り出しに id を利用していますが、
id 以外のカラムを用いて取り出す方法も覚えておくと便利です。

コンソールを起動してください。 ( rails c )
下記を参考に色々と試してみてください。

railsConsole
>> Book.all
   #bookテーブルのすべてのデータを取得します。
>> Book.find_by(title: "人間失格")
   #条件に合ったデータを、1件だけ取得します。
>> Book.where(country: "jp")
   #条件に合ったデータを、すべて取得します。
>> Book.where(country: "jp").where(year: 1905)
   #AND検索。 和書で 1905年出版の本を取得しています。
>> Book.where("(country = ?) OR (year = ?)","jp",1905)
   #OR検索。 和書 または 1905年出版の本を取得しています。
>> Book.order(create_at: :asc)
   #並び順を指定します。 作成日時の昇順で取得しています。
>> Book.where(user_id: 1).order(id: :desc)
   #user_id 1さんの蔵書を、idの降順で取得しています。 

where の利用

検索条件を学んだのでこれ使って、蔵書管理アプリケーションらしく
個人の蔵書を一覧表示する機能を構築してみましょう。

app/controllers/books_controller.rb
def show
#   @book = Book.find(params[:id]) コメントアウトしました。
    @book = Book.where(user_id: params[:id])
end

user_id が パラメーター番号の人の蔵書データをすべて取得して@bookに格納しています。
View も編集していきましょう。 @book のなかに複数のデータがある場合(連想配列)
each を用いて、順に表示させていくのでしたね。(2章の復習です)

app/views/books/show.html.erb
<% @book.each do |book| %>
  <p>本のタイトル: <%= book.title %></p>
  <p>著者: <%= book.author %></p>
<% end %>

http://localhost:3000/books/show/1
にアクセスしてみると、 user_id:1 になっている本のデータがすべて表示されました。

 本のタイトル: 吾輩は猫である
 著者: 夏目漱石

 本のタイトル: Alice's Adventures in Wonderland
 著者: Lewis Carroll

【補足】「あれ? has_many とかつけなくてよいんだっけ?」
と思った方は、前章の内容を覚えてくれていますね。
ただ少し勘違いがあります。 has_many,belongs_to は異なるテーブルの紐付けに使います。
今回は、 Book Modelを経由し、Bookテーブルだけにアクセスしているので不要です。
User Model から、Bookテーブルのデータを取り出すならば必要になってきます。

データ登録フォームの作成

さて、データベースから情報を取り出したり加工したりする方法は覚えました。
しかしこのままではユーザーは、本を登録することができません。

本を登録するための機能を作成する方法を学んでいきましょう。

登録形式は、フォーム。
URL は http://localhost:3000/books/new
とします。

form_for

データベースに新しいデータを保存したいので、 Model を呼び出しましょう。
場所はどこに書きましょう。
new.html.erb に登録フォームを置くので、 new アクションの中ですね。
新しいデータを登録するときは、 Model名.new とします。下記のコードを追記してください。

app/controllers/books_controller.rb
  def new
    @book = Book.new
  end

new.html.erb に 入力フォームを作成していきましょう。
rails では、フォームを簡単につくれる form_forヘルパーというものがあります。

app/views/books/new.html.erb
<%= form_for(@book) do |f| %>
  <%= f.label :country %>
  <%= f.text_field :country %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.label :author %>
  <%= f.text_field :author %>

  <%= f.label :year %>
  <%= f.number_field :year %>

  <%= f.label :user_id %>
  <%= f.number_field :user_id %>

  <%= f.label :comment %>
  <%= f.text_area :comment %>

  <%= f.submit "送信" %>
<% end %>

form_for に関する詳しい使い方は、こちらのサイトで学習してください。
form_forの使い方を徹底解説!

ここでは重要な部分だけ、簡単に解説します。
上の form_forヘルパーにより、下記のような HTML が生成されます。(少しシンプル化してます)

new.html
<form class="new_book" id="new_book" action="/books" method="post">
   <label for="book_country">Country</label>
   <input type="text" name="book[country]" id="book_country" />

   <label for="book_title">Title</label> 
   <input type="text" name="book[title]" id="book_title" /> 

   <label for="book_author">Author</label> 
   <input type="text" name="book[author]" id="book_author" /> 

   <label for="book_year">Year</label> 
   <input type="number" name="book[year]" id="book_year" /> 

   <label for="book_user_id">User</label> 
   <input type="number" name="book[user_id]" id="book_user_id" /> 

   <label for="book_comment">Comment</label> 
   <textarea name="book[comment]" id="book_comment">  </textarea> 

   <input type="submit" name="commit" value="送信" /> 
 </form> 

ここで重要な属性は、
< form class="new_book" id="new_book" action="/books" method="post" >
の中の、 action="/books"method="post" の2つです。

この2つは送信ボタンが押されたら、
/books に対して、 POST リクエストを送信する
という指示をしています。

なので、 /books 対して、 POST リクエストがあった場合の処理を書いていきましょう。
送信したあとは、books Controller の create アクションに飛ばしたいです。
(のちほどデータベースに保存する処理を create アクションに書いていきます)

post 'books', to: 'books#create'
コードはこのようになるので、 routes.rb に追記しましょう。
(ルーティング関係は、 routes.rb でしたね)

config/routes.rb
Rails.application.routes.draw do
  get 'books/index'
  get 'books/show/:id', to: 'books#show'
  get 'books/new'
  post 'books', to: 'books#create'
end

index と new は to: が書かれていませんね。
前章でも説明したとおり、Railsは、アクションが呼び出されると、
自動的に アクション名.html.erbが呼び出されるので、
下記と同じ意味になります。
get 'books/index' to: 'books#index'
get 'books/new', to: 'books#new'

1,登録ボタンが押される
2,フォームに入力されたデータを、create アクションに飛ばす

という流れで、データが動いているので最後に、create アクションの中で
情報をデータベースに保存する処理を書きます。Modelを呼び出します。

書き込みの仕方は、seed.rb でもやりましたね。

seed.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

 【備考】
 ちなみに、このままなにも受け取り処理を書かずに情報を送信してみるとどうなるでしょうか。
 試しに、本の情報をフォームに入力し送信してみると、なにも動きません。

 送信しているので、フォームから パラメーターは飛んでいます。
 このパラメーターですが、実は見ることが出来るんです。

 rails s を起動している、 terminalをみてください。
 いつの間にか、いろんな文字が出力されていますね。
 サーバーが受け取ったデータなどの情報が表示されています。
 この先、困ったときにここをみると解決できることがあると思います。

 "どういったデータがどういう形式で動いているかは、サーバーのログをみる"
 自分でサービスをつくるのに、重要なテクニックなので覚えておいてください

 

 
サーバーログの一番下をみてみましょう。(下図は、ややシンプル化しています)

terminal
Started POST "/books"
Processing by BooksController#create as HTML
  Parameters: {"book"=>{"country"=>"jp", "title"=>"吾輩は猫である", "author"=>"夏目漱石",
               "year"=>"1905", "user_id"=>"1", "comment"=>"おもしろかった"}}

パラメーターをみると、二重の連想配列になっているのがわかると思います。
( parameters{} の中に book{} が入っている )

通常の連想配列の場合は、params[:title] として取得できていました。
二重の連想配列の場合は、params[:book][:title] とすることで取得できます。
bookキー の中のtitleキー のデータを取得しています )
 連想配列について詳しくはこちらを参照してください Array(配列)とHash(連想配列)入門

さて、ここまで理解できていれば create アクションの中身も想像できると思います。
( seed.rb の、本の情報が書かれていた部分をパラメーターの中身に書き換えるだけです )

app/controllers/books_controller.rb
  def create
    @book = Book.new
    @book.country = params[:book][:country]
    @book.title = params[:book][:title]
    @book.author = params[:book][:author]
    @book.year = params[:book][:year]
    @book.user_id = params[:book][:user_id]
    @book.comment = params[:book][:comment]
    @book.save
    redirect_to '/books/show/1'
  end

最後の、redirect_to '/books/show/1' の部分ですが、
登録ボタンを押したあとに、画面遷移してほしいので、リダイレクト先を指定しました。

http://localhost:3000/books/new ここで本の情報を登録
http://localhost:3000/books/show/1 ここで登録内容を確認
(Userの欄を、1以外にした人は、該当ユーザーのページを開いてください)

課題

1, show ページに、タイトルと著者以外の情報も表示してください。
2, リダイレクト先を、 http://localhost:3000/books/index に変更してください。
3, index ページに、全蔵書のタイトルを表示させてださい。

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

Before Rails Tutorial3章 Scaffold を用いない開発

本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用

前章では、Scaffoldを用いて高速でアプリケーションを構築しました。
自動化された部分も多く、現段階では、大まかな理解であるとおもいます。

今回は、route Model View Controller それぞれ自分で作っていきます。
とはいっても、継承まで含めてすべて書くには理解することが膨大で大変ですので、
前回も使用した generate は使用していきます。

まずは、Railsの新しいアプリケーションを作成します。
1章:環境構築の内容を参考に作成してみましょう。

terminal
$ cd ~/rails_app
$ mkdir bukukore
$ bundle init
   (Gemfileにgem "rails","~> 5.2.3"を追加し保存してください)
$ bundle install --path vendor/bundlesa
$ bin/rails s

http://localhost:3000/ にアクセスして表示されていればokです。

$ rails s -p 3100 などとポートを変更することでサーバーを複数起動可能です。
この場合は http://localhost:3100/ にアクセスします。

MVC 復習

前章では、アプリ制作を通して、MVCの流れを理解してきました。

Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

前回、制作したながれを図を見ながら復習していきます。
それぞれの番号が、コードではどうなっていたか思い出しながら見てください。

MVC-02.png

1, ブラウザから /users へのリクエストを Rails sever に送信する
2, router によって、/users は Users controller の index アクションを呼び出す
3, index アクションから、User Model が呼び出される
4, User Model は DataBase からデータを取り出す
5, 取り出したデータを、Controller に返す
6, 受け取ったデータを @users に保存し、 View (indexアクションなので index.html.erb) に渡す。
7, ERBを実行し、@users のデータを含んだ HTMLを生成し、Controller へ返す
8, Controller は、受け取ったHTMLをブラウザにわたす

今回も Model View Controller を構築していくので、適宜図を見返してください。

Controller と View の作成

作成は基本的に generate コマンドを用います。
(gは generateコマンドの短縮形です)
g の後は、controller に続けて、コントローラーの名前(複数形)を書きます。
rails g controller Books index show new
そのうしろ(引数といいます)に書いたものは、同名のviewとアクションが自動生成されます。

tarminal
$ bin/rails g controller Books index show new
     create    app/controllers/books_controller.rb
     route     get 'books/index'
               get 'books/show'
               get 'books/new'
     create    app/views/books
     create    app/views/books/index.html.erb
     create    app/views/books/show.html.erb
     create    app/views/books/new.html.erb

ルーティングが生成されているので、確認してみましょう。

terminal
$ bin/rails routes
  Verb  URI             Pattern
  GET   /books/index    books#index
  GET   /books/show     books#show
  GET   /books/new      books#new

これは具体的には、 http://localhost:3000/books/index
というURLに対するリクエストがあれば、 books_controller の index アクション を呼び出すというものでした。
今回アクションの中身が空なので、Model は呼び出されず、単に対応するView (index.html.erb) が実行されるだけです。

ルーティングがうまくいってみるか確認するために、試しにアクセスしてみましょう。
http://localhost:3000/books/index

Books#index
Find me in app/views/books/index.html.erb

と表示されているとおもいます。

Viewの変更

erbの変更は前回行ったとおりですね。とてもシンプルなものでした。
Books#index の部分を、全蔵書一覧などに変更しておきましょう。

app/views/books/index.html.erb
<h1>全蔵書一覧</h1>

 erb の中身がシンプルすぎて不思議な方もいらっしゃるかもしれません。
 【気になって次にすすめない人向けの解説】
  app/views/layouts/application.html.erb を開いてください。
 コード内の、<%= yiels %> の部分に、 index.html.erb が移植されて HTML を生成しています。 
 いまはこの程度の理解で問題ありません。 https://railstutorial.jp/ でより深く理解できます。

Controllerの変更

コントローラーの中にアクションが本当に作られているかも確認してみましょう。
app/controllers/books_controller.rb を開いてみてください。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end
end

データベース内のデータを取り出すには、Controllerのアクションから
Model を呼び出せば良いのでした。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
   @book = Book.find(1)
  end

  def new
  end
end

このままではエラーがでます。(Modelも、本の情報も作っていないので)
generate コマンドを使用して、Model を作成していきます。

Model の作成

booksテーブルには、前章と同じカラムを持たせていきます。

terminal
$ bin/rails g model Book country:text title:text author:text year:integer user_id:integer comment:text
      create    db/migrate/20200202020202_create_books.rb
      create    app/models/book.rb

g の後は、model に続けて、Model名(単数形)を書きます。
データベースにテーブルを作成するには migrate を実行するのでしたね。

terminla
$ bin/rails db:migrate
== 20200202020202 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0008s
== 20200202020202 CreateBooks: migrated (0.0009s) =============================

補足. migrate はマイグレーションファイルを実行しています。
db/migrate/(作成日時)_create_books.rb というファイルです。
ひらいてみると、下記のようなコードがあると思います。

class CreateBooks < ActiveRecord
 def change
   create_table :books do |t|
    t.text :country
    t.text :title
    t.text :author
    t.integer :year
    t.integer :user_id
    t.text :comment
    t.timestamps
   end
 end
end

上記は text型であるcountryというカラム(他6つ)を持つ books テーブルを作成
せよという意味です。 change 意外に up down などもあります。詳細は下記サイトなどを参照ください
https://www.sejuku.net/blog/14229

これでbooksテーブルが作成されました。

テーブルは作成したけども、本の情報はまだなにもいれてませんね。
初期データをいれるには db/seeds.rb ファイルを使用します。
( seed = 種 という意味です )
大量のデータを入力するのは手間なのでseed.rbを作成していきます。
(そもそもブラウザからデータをいれる機能もまだ作っていません)

下記の seed ファイルを実行すると、bookテーブルに本の情報が3件保存されます

db/seeds.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

@book = Book.new
@book.country = "jp"
@book.title = "人間失格"
@book.author = "太宰治"
@book.year = 1948
@book.user_id = 2
@book.comment = "すばらしかった"
@book.save

@book = Book.new
@book.country = "gb"
@book.title = "Alice's Adventures in Wonderland"
@book.author = "Lewis Carroll"
@book.year = 1865
@book.user_id = 1
@book.comment = "Amazing"
@book.save

seed ファイルは、下記のコマンドで実行できます。

terminal
$ bundle exec rake db:seed

データが保存されたかの確認は。rails c でコンソールを起動すると確認できます。

railsConsole
@book = Book.find(1)

ブラウザ(show)で表示してみましょう。

app/view/books/show.html.erb
<p>本のタイトル: <%= @book.title %></p>
<p>著者: <%= @book.author %></p>

本のタイトル: 吾輩は猫である

著者: 夏目漱石

と表示されていればOKです。

課題1,

@book はどこで定義したか、再確認してください。

URLからパラメーターの取得

本のデータを取り出して表示するところまでうまくいきました!
すごい進歩です!

ただ、まだ不満点があります。
それは、 id:1 の本のデータしか表示できていない点です。
理由は、Contoroller の show アクションをみると、
@book = Book.find(1)
となっているためです(id:1 の本の情報しか見つけてきていない)

ここをidパラメーターが1以外も入力できるように変更するにはどうすればよいでしょうか。
@book = Book.find(params[:id])
とすることで、1以外のパラメーターを見つけてこれます。

app/controllers/books_controller.rb
 def show
    @book = Book.find(params[:id])
 end

パラメーター(数値)は、どこで指定すればよいでしょうか。
URL にパラメーターを含めて渡すテクニックがあるので覚えておきましょう。
例)ID:2の本の情報を取得したい場合 http://localhost:3000/books/show/2
  このままではルーティングエラーが表示されます。
  books/show へのリクエストに関しては記述していますが、
  books/show/(数字) へのリクエストについてはなにも書いていない為です。

config/routes.rb を開いてください。
URLに含まれる数字をパラメーターとして取得するには、下記のように記述します。

config/routes.rb
#   get 'books/show'(# でコメントアウトしてます)
get 'books/show/:id', to:'books#show'

こうすることで /books/show/2 の 2の部分が、:id パラメーターとして利用可能になります。
( 利用するときは、params[:id] とかきます)

to: 'books#show' 
books Cコントローラーの show アクションを呼び出す指示になります。

これで、任意のidの本情報を取得できるようになりました。

http://localhost:3000/books/show/1
http://localhost:3000/books/show/2

Model 検索

いま本の情報の取り出しに id を利用していますが、
id 以外のカラムを用いて取り出す方法も覚えておくと便利です。

コンソールを起動してください。 ( rails c )
下記を参考に色々と試してみてください。

railsConsole
>> Book.all
   #bookテーブルのすべてのデータを取得します。
>> Book.find_by(title: "人間失格")
   #条件に合ったデータを、1件だけ取得します。
>> Book.where(country: "jp")
   #条件に合ったデータを、すべて取得します。
>> Book.where(country: "jp").where(year: 1905)
   #AND検索。 和書で 1905年出版の本を取得しています。
>> Book.where("(country = ?) OR (year = ?)","jp",1905)
   #OR検索。 和書 または 1905年出版の本を取得しています。
>> Book.order(create_at: :asc)
   #並び順を指定します。 作成日時の昇順で取得しています。
>> Book.where(user_id: 1).order(id: :desc)
   #user_id 1さんの蔵書を、idの降順で取得しています。 

where の利用

検索条件を学んだのでこれ使って、蔵書管理アプリケーションらしく
個人の蔵書を一覧表示する機能を構築してみましょう。

app/controllers/books_controller.rb
def show
#   @book = Book.find(params[:id]) コメントアウトしました。
    @book = Book.where(user_id: params[:id])
end

user_id が パラメーター番号の人の蔵書データをすべて取得して@bookに格納しています。
View も編集していきましょう。 @book のなかに複数のデータがある場合(連想配列)
each を用いて、順に表示させていくのでしたね。(2章の復習です)

app/views/books/show.html.erb
<% @book.each do |book| %>
  <p>本のタイトル: <%= book.title %></p>
  <p>著者: <%= book.author %></p>
<% end %>

http://localhost:3000/books/show/1
にアクセスしてみると、 user_id:1 になっている本のデータがすべて表示されました。

 本のタイトル: 吾輩は猫である
 著者: 夏目漱石

 本のタイトル: Alice's Adventures in Wonderland
 著者: Lewis Carroll

【補足】「あれ? has_many とかつけなくてよいんだっけ?」
と思った方は、前章の内容を覚えてくれていますね。
ただ少し勘違いがあります。 has_many,belongs_to は異なるテーブルの紐付けに使います。
今回は、 Book Modelを経由し、Bookテーブルだけにアクセスしているので不要です。
User Model から、Bookテーブルのデータを取り出すならば必要になってきます。

データ登録フォームの作成

さて、データベースから情報を取り出したり加工したりする方法は覚えました。
しかしこのままではユーザーは、本を登録することができません。

本を登録するための機能を作成する方法を学んでいきましょう。

登録形式は、フォーム。
URL は http://localhost:3000/books/new
とします。

form_for

データベースに新しいデータを保存したいので、 Model を呼び出しましょう。
場所はどこに書きましょう。
new.html.erb に登録フォームを置くので、 new アクションの中ですね。
新しいデータを登録するときは、 Model名.new とします。下記のコードを追記してください。

app/controllers/books_controller.rb
  def new
    @book = Book.new
  end

new.html.erb に 入力フォームを作成していきましょう。
rails では、フォームを簡単につくれる form_forヘルパーというものがあります。

app/views/books/new.html.erb
<%= form_for(@book) do |f| %>
  <%= f.label :country %>
  <%= f.text_field :country %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.label :author %>
  <%= f.text_field :author %>

  <%= f.label :year %>
  <%= f.number_field :year %>

  <%= f.label :user_id %>
  <%= f.number_field :user_id %>

  <%= f.label :comment %>
  <%= f.text_area :comment %>

  <%= f.submit "送信" %>
<% end %>

form_for に関する詳しい使い方は、こちらのサイトで学習してください。
form_forの使い方を徹底解説!

ここでは重要な部分だけ、簡単に解説します。
上の form_forヘルパーにより、下記のような HTML が生成されます。(少しシンプル化してます)

new.html
<form class="new_book" id="new_book" action="/books" method="post">
   <label for="book_country">Country</label>
   <input type="text" name="book[country]" id="book_country" />

   <label for="book_title">Title</label> 
   <input type="text" name="book[title]" id="book_title" /> 

   <label for="book_author">Author</label> 
   <input type="text" name="book[author]" id="book_author" /> 

   <label for="book_year">Year</label> 
   <input type="number" name="book[year]" id="book_year" /> 

   <label for="book_user_id">User</label> 
   <input type="number" name="book[user_id]" id="book_user_id" /> 

   <label for="book_comment">Comment</label> 
   <textarea name="book[comment]" id="book_comment">  </textarea> 

   <input type="submit" name="commit" value="送信" /> 
 </form> 

ここで重要な属性は、
< form class="new_book" id="new_book" action="/books" method="post" >
の中の、 action="/books"method="post" の2つです。

この2つは送信ボタンが押されたら、
/books に対して、 POST リクエストを送信する
という指示をしています。

なので、 /books 対して、 POST リクエストがあった場合の処理を書いていきましょう。
送信したあとは、books Controller の create アクションに飛ばしたいです。
(のちほどデータベースに保存する処理を create アクションに書いていきます)

post 'books', to: 'books#create'
コードはこのようになるので、 routes.rb に追記しましょう。
(ルーティング関係は、 routes.rb でしたね)

config/routes.rb
Rails.application.routes.draw do
  get 'books/index'
  get 'books/show/:id', to: 'books#show'
  get 'books/new'
  post 'books', to: 'books#create'
end

index と new は to: が書かれていませんね。
前章でも説明したとおり、Railsは、アクションが呼び出されると、
自動的に アクション名.html.erbが呼び出されるので、
下記と同じ意味になります。
get 'books/index' to: 'books#index'
get 'books/new', to: 'books#new'

1,登録ボタンが押される
2,フォームに入力されたデータを、create アクションに飛ばす

という流れで、データが動いているので最後に、create アクションの中で
情報をデータベースに保存する処理を書きます。Modelを呼び出します。

書き込みの仕方は、seed.rb でもやりましたね。

seed.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

 【備考】
 ちなみに、このままなにも受け取り処理を書かずに情報を送信してみるとどうなるでしょうか。
 試しに、本の情報をフォームに入力し送信してみると、なにも動きません。

 送信しているので、フォームから パラメーターは飛んでいます。
 このパラメーターですが、実は見ることが出来るんです。

 rails s を起動している、 terminalをみてください。
 いつの間にか、いろんな文字が出力されていますね。
 サーバーが受け取ったデータなどの情報が表示されています。
 この先、困ったときにここをみると解決できることがあると思います。

 "どういったデータがどういう形式で動いているかは、サーバーのログをみる"
 自分でサービスをつくるのに、重要なテクニックなので覚えておいてください

 

 
サーバーログの一番下をみてみましょう。(下図は、ややシンプル化しています)

terminal
Started POST "/books"
Processing by BooksController#create as HTML
  Parameters: {"book"=>{"country"=>"jp", "title"=>"吾輩は猫である", "author"=>"夏目漱石",
               "year"=>"1905", "user_id"=>"1", "comment"=>"おもしろかった"}}

パラメーターをみると、二重の連想配列になっているのがわかると思います。
( parameters{} の中に book{} が入っている )

通常の連想配列の場合は、params[:title] として取得できていました。
二重の連想配列の場合は、params[:book][:title] とすることで取得できます。
bookキー の中のtitleキー のデータを取得しています )
 連想配列について詳しくはこちらを参照してください Array(配列)とHash(連想配列)入門

さて、ここまで理解できていれば create アクションの中身も想像できると思います。
( seed.rb の、本の情報が書かれていた部分をパラメーターの中身に書き換えるだけです )

app/controllers/books_controller.rb
  def create
    @book = Book.new
    @book.country = params[:book][:country]
    @book.title = params[:book][:title]
    @book.author = params[:book][:author]
    @book.year = params[:book][:year]
    @book.user_id = params[:book][:user_id]
    @book.comment = params[:book][:comment]
    @book.save
    redirect_to '/books/show/1'
  end

最後の、redirect_to '/books/show/1' の部分ですが、
登録ボタンを押したあとに、画面遷移してほしいので、リダイレクト先を指定しました。

http://localhost:3000/books/new ここで本の情報を登録
http://localhost:3000/books/show/1 ここで登録内容を確認
(Userの欄を、1以外にした人は、該当ユーザーのページを開いてください)

課題

1, show ページに、タイトルと著者以外の情報も表示してください。
2, リダイレクト先を、 http://localhost:3000/books/index に変更してください。
3, index ページに、全蔵書のタイトルを表示させてださい。

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

あとからgem sqlite3を削除したらエラーが出た

rails new するときに '-o' コマンドを入れてなくて、あとからgem 'sqlite3'を消したら以下のエラーが出た

ActiveRecord::ConnectionNotEstablished: No connection pool with 'primary' found.

環境はRails5.2

まずは config/application.rb から

require "ative_record/railtie"

をコメントアウト。

次に、config/environments/development.rb 内を "active_record"で検索して

config.active_record.migration_error = :page_load
config.active_record.verbose_query_logs = true

この2つをコメントアウト!
多分これで問題ない!

おっと。
production.rbの方もコメントアウトしなければ。。。

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

RailsアプリでFacebook Twitter GitHubのOAuth認証を可能にした

表題のとおりです。
とりあえず、主要SNSと個人的に入れたかったGitHubを対応させました。
パラメータそれ自体は汎用性が高いので、今後その他のサービスの認証機能も用意に対応できそうな感じ。

注意

この記事は、1年ほど前にQiitaに投稿したものを、同じ内容で再投稿したものになります。
そのため、内容が古くなっている可能性があります。

以下のコードは、Herokuにデプロイをする前提で書いています。

参考

RailsでFacebookログインを実装する
【Rails4.2.x】omniauth(twitter/facebook/github)実装まとめ
Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ

正直コーディングそのものより、API申請の方が大変だったような・・・

必要なGemの準備

Gemfile
# OAuth
gem 'omniauth'
gem 'omniauth-github'
gem 'omniauth-twitter'
gem 'omniauth-facebook'

# 本番環境と開発環境でCredentialsを分けるために使用
group :development, :test do
  gem 'rails-env-credentials'
end

Userモデルを作成

下記の構成のUserモデルを作りました。

schema.rb
# $ be rails g model user name provider uid:text oauth_token icon email 

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "provider"
    t.text "uid"
    t.string "oauth_token"
    t.string "icon"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

その他に、例えばTwitterのプロフィールにあるような『住所』カラムが欲しい場合自由に追加してOKです。

omniauth.rbを作成

config/initializersに以下のファイルを追加。

config/initializers
Rails.application.config.middleware.use OmniAuth::Builder do
    provider :github, Rails.application.credentials.github[:key], Rails.application.credentials.github[:secret], scope: "user,repo"
    provider :twitter, Rails.application.credentials.twitter[:key], Rails.application.credentials.twitter[:secret]
    provider :facebook, Rails.application.credentials.facebook[:key], Rails.application.credentials.facebook[:secret]
end

上記コードでは、APIの ClientIDClientSecret を、Credentialsなる機能を使って管理しています。
Credentialsだと、本番環境の秘匿情報しか扱えなかったので、便利なGem"rails-env-credentials"を使って、開発環境でもCredentialsを使えるようにしました。

Rails 5.2 の Credentials を各環境でも使えるようにする gem を作りました

Gem : rails-env-credentials

Credentialsの中身

本番用・開発用どちらも同じように ClientID と ClientSecret を設定します。
EDITOR="nano" be rails env_credentials:edit
EDITOR="nano" be rails credentials:edit

config/credentials.uml.enc
github:
    key: xxxxxxxxxxxxx
    secret: XXXXXXXXXXXXXXXXXXXxX

twitter:
    key: xxxxxxxxxxxxx
    secret: XXXXXXXXXXXXXXXXXXXxX

facebook:
    key: xxxxxxxxxxxxx
    secret: XXXXXXXXXXXXXXXXXXXxX

このとき、TwitterとGitHubに関しては、本番用アプリと開発用アプリのそれぞれで、別の ID と Secret を取得しました。

Facebookの方は、そもそも非暗号化通信ではAPIを使用できないみたいなので、とりあえずエラー回避のために本番用と同じ ID/Secret を、開発用Credentialsに書いておきました。

(つまり、FacebookのOAuth認証は、本番環境でしか行なえません)

ローカルでFacebookのOAuth認証テストを行う場合は、少し面倒くさい手順が必要のようです。
Facebookログインがhttps必須になったので、localhost開発でも対応する

model/user.rbを編集

model/user.rbを以下に書き換えます。

model/user.rb
class User < ApplicationRecord
    def self.find_or_create_from_auth(auth)
        email = auth[:info][:email]
        provider = auth[:provider]
        uid = auth[:uid]
        icon = auth[:info][:image]
        oauth_token = auth[:credentials][:token]
        case provider
        when "twitter", "facebook" then
            name = auth[:info][:name]
        when "github" then
            name = auth[:info][:nickname]
        end

        self.find_or_create_by(provider: provider, uid: uid) do |user|
            user.email = email
            user.name = name
            user.icon = icon
            user.provider = provider
            user.uid = uid
            user.oauth_token = oauth_token
        end
    end
end

例えば先程、Userモデルを作った際にLocationカラムも追加した場合、このuser.rbに下記を追記すれば対応できるはずです・

        location = auth[:info][:location]
        self.find_or_create_by(provider: provider, uid: uid) do |user|
            user.location = location
        end

一部、Case文でprovider毎にパラメータを変えなければならない箇所が出てきます。

login処理

sessioinsControllerを作成し、以下のコードを追記します。

class SessionsController < ApplicationController
    def create
        auth = request.env['omniauth.auth']
        user = User.find_or_create_from_auth(request.env['omniauth.auth'])

        session[:user_id] = user.id
        redirect_to root_path
    end

    def destroy
        reset_session
        redirect_to root_path
    end
end

application_controller.rbも編集。

class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception
    helper_method :current_user, :logged_in?

    private

    def current_user
        return unless session[:user_id]
        @current_user ||= User.find(session[:user_id])
    end

    def logged_in?
        !!session[:user_id]
    end

    def authenticate
        return if logged_in?
        redirect_to root_path, alert: 'ログインしてください'
    end
  end

View

config/route.rb

config/route.rb
Rails.application.routes.draw do
  get 'auth/:provider/callback', to: 'sessions#create'
  get '/logout', to: 'sessions#destroy'

  root 'homes#top'
end

html

<% if logged_in? %>
  <%= link_to 'log out', '/logout' %>
  <%= @user.name %>さん、こんにちは。
  <%= image_tag(@user.icon) %>
<% else %>
  <%= link_to 'GitHubアカウントでログイン', '/auth/github' %>
  <%= link_to 'Twitterアカウントでログイン', '/auth/twitter' %>
  <%= link_to 'Facebookアカウントでログイン', '/auth/facebook' %>
<% end %>

完成

最後に、HerokuでCredentialsを使うために下記を実行。

heroku config:set RAILS_MASTER_KEY=`cat config/master.key`

思った以上に各APIでパラメータが重複してて楽に実装できました!
先人の方々の知恵に本当に感謝です。

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

resourcesメソッドはとっても便利!

はじめに

この記事はプログラミング初学者である私自身の備忘録として残します。

プログラミングの学習を初めてからというもの、ルーティングの記述ではずっとresourcesメソッドを使い続けていたので、なぜこのメソッドを使うのかおさらいしてみました。結論「やっぱり便利だな〜」。

ルーティング(routes.rb)の記述に際して

resourcesメソッドの 不使用時使用時 のコードを比べてみました。

あれ?そもそもルーティングってなんだっけ?って思った方は、こちらを読んでみてください。

### resourcesメソッド ** 不使用時 **
root to: 'posts#index'
get '/posts',     to: 'posts#index'
get '/posts/new', to: 'posts#new'
post '/posts',    to: 'posts#create'
### resourcesメソッド ** 使用時 **
root to: 'posts#index'
resources :posts, only: [:index, :new, :create]

resourcesメソッド不使用時
get '/posts', to: 'posts#index' を説明すると、

HTTPメソッドのgetを使ってposts パス(URIパターン)を取得した際の処理として、postsコントローラのindexアクションを指定する。

という意味です。
形としては

HTTPメソッド 'パス(URIパターン)', to: 'コントローラー名#アクション名'

てなかんじです。

resourcedメソッド使用時resources :posts, only: [:index, :new, :create]を説明すると、

postsコントローラーを参照(resource)して、コントローラーが持つ7つのアクションからindexnewcreateのみを使う。

という意味です。
形としては

resources :コントローラー名, only: [:アクション名, :アクション名・・・]

となります。
実はこれ、onlyを省けば7つのアクションすべて使えるという意味になります。

まとめ

どちらも同じ処理を行うコードですが、
resourcesメソッド不使用時では3行使って記述されているコードが、使用時ではたった1行にまとめられています。さらにもう一点、resourcesメソッドを使えば、HTTPメソッドパス(URIパターン)を指定する必要がありません。これだけで十分このメソッドの便利さが分かると思います。

HTTPメソッドパス(URIパターン)について気になった方は、自分が作業中のファイルをターミナルで開いて、rails routesで確認してみてください。(言葉の意味が分からない場合は文字をクリック)

もっと詳しく知りたい方は

以下にリンクを貼っておきます。

・Samurai Blog
【Rails入門】resourcesの使い方まとめ

・Pikawaka
【Rails】resourcesメソッドを徹底解説!

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