20200124のRailsに関する記事は13件です。

Rails Javascript/Html: 投稿編集画面で写真の表示を確認したい!

= f.file_field :image, id: :message_img, placeholder: "写真の貼り付け:" 以下にJavaScriptを配置

  :javascript 
    $(function() {
     function readURL(input) {
      if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
          $('#img_prev').attr('src', e.target.result);
        }
        reader.readAsDataURL(input.files[0]);
      }
     }
     $("#message_img").change(function(){
       readURL(this);
     });
    });

・img_prevの属性操作
・changeメソッドでmessage_imgの読み込みURLを変更する

.field__img-show

  = image_tag @message.image.url, id: :img_prev,size: '800x600' if @message.image?

最後にimage_tagで変更されたimg_prevを表示。
つぎはぎの知識でなんとか起動させることができました^^;

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

Railsの思想

はじめに

「パーフェクト Ruby on Rails」に Rails の特徴、強く打ち出している思想について始めのほうに記載されていたので「パーフェクト Ruby on Rails」を振り返り、メリットを知りたいと思いましたのでまとめます。

CoC(Convention over Configuration)

直訳では「設定より規約」になります。有名です。
従来のアプリケーションフレームワークには規約がなく、開発者が自由に設定ができますが、Railsはファイル名の規約やテーブル定義の規約など様々な規約があります。

パーフェクト Ruby on Railsの例(そのままではないです)

  • 社員情報を表現するモデルをEmployeeモデルとした場合、Railsで規約には則って開発することが求められる
    • データベースのテーブル名はモデル名の複数形のEmployeesにする
    • /employeesというURLは社員の一覧を表す
    • /employees/1というURLは社員ID:1の社員情報を表す

✔️メリット

  • 規約に則って記述することで、設定のファイルを書く必要がない
  • プロジェクトごとに設定の差異を生んでしまうことがない
  • 共通のルールなので、他のエンジニアとコミュニケーションが取りやすい
  • 開発者が決定すべきことが減る

DRY(Don’t Repeat Yourself)

「繰り返してはいけない」「同じことは繰り返さない」と訳されます。
同じソースコードを統一し短くコードを書く事を推奨する思想があります。
情報の重複をなくし、1つのことは1箇所に記述します。

✔️メリット

  • DRY原則を守ることにより、コードを変更する際に何箇所も変更する必要がなくなる

REST(REpresentational State Transfer)

Webのアプリケーション設計思想のひとつです。
RESTfulという言葉をよく目にしますが、RESTという設計思想に則ったアーキテクチャのことをいいます。

パーフェクト Ruby on Railsの例(そのままではないです)

  • /employees/1というURIが指す内容が「リソース」とすると
    • /employees/1にHTTPのGETメソッドでアクセスすることで、社員情報を取得する処理を行える
    • /employees/1にHTTPのDELETEメソッドでアクセスすることで、社員情報を削除する処理を行える

✔️メリット

  • アプリケーション中のリソースがURIによって参照できる
  • 機能追加がしやすい自然な設計になる
  • CRUD操作と、4つの基本的なHTTPrequestメソッド(GET,POST,PUT,DELETE)の両方に対応している

おしまい

 「パーフェクト Ruby on Rails」を進められ最近読み始めました。話題がたくさんで読み応えがあると聞いたので、読み進めて知識を増やしたいですね〜!頑張ります。
ちょっとだけRails tutorialも参考にいたしました。

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

Action Cableの設定でつまずいたこと

行っていること

以下の記事や動画を参考にチャット機能を作成しています。
Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より)
0から手を動かして作るRailsチャットアプリ【チュートリアル】

リアルタイムでのチャット機能を作成する際につまずいたことを、記録も兼ねて共有します。

やりたいこと

自分(current_user)と他の人で表示方法を変える
スクリーンショット 2020-01-24 17.56.47.png

チャンネルの作成

$ rails g channel chatroom speak
    create  app/channels/chatroom_channel.rb
    create  app/assets/javascripts/channels/chatroom.coffee

つまずいたこと

chatroom_channel.rbから_post.html.erbをrenderで呼び出そうとすると、[ActionView::Template::Error - undefined method 'id' for nil:NilClass] と言われる。→channelからrenderした際にcurrent_userの値を取れていないため

コード一覧

routes.rb
Rails.application.routes.draw do
  root to: 'toppages#index'
  # (省略)

  resources :chatrooms do
    member do
      get :users
    end
  end

  mount ActionCable.server => '/cable'
end
posts/_post.html.erb
<% unless post.user_id == current_user.id %>
  <p style="color:blue;"><%= post.content %></p>
<% else %>
  <p style="color:red;"><%= post.content %></p>
<% end %>
chatroom_channel.rb
class ChatroomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chatroom_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    message = Post.create! content: data['message'], user_id: data['user_id'], chatroom_id: data['room_id']
    templete = ApplicationController.renderer.render(partial: 'posts/post', locals: { post: message })
    ActionCable.server.broadcast 'chatroom_channel', message: templete
  end
end
chatroom.coffee
# クライアントサイドの処理を受け持つチャンネ
App.chatroom = App.cable.subscriptions.create "ChatroomChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    $('#posts').append data['message']


    # Called when there's incoming data on the websocket for this channel

  speak: (content, data_user, data_room) ->
    @perform 'speak', message: content, user_id: data_user, room_id: data_room


document.addEventListener 'DOMContentLoaded', ->
  input = document.getElementById('chat_input') 
  data_user = input.getAttribute("data_user")
  data_room = input.getAttribute("data_room")
  button = document.getElementById('chat_button')
  button.addEventListener 'click', ->
    content = input.value
    App.chatroom.speak(content, data_user, data_room)
    input.value = ''
    return
  return

原因

channelからは sessionを使うことが出来ないそうです。
(sessions_helperでcurrent_userを定義している。)

解決方法

channelからcookieは使うことができるそうなので、cookieからユーザー情報を取ってきて、current_user変数に代入。
主にこちらの記事を参考にしました。

cookieの情報からcurrent_userの作成

channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if verified_user = User.find_by(id: session['user_id'])
        verified_user
      else
        reject_unauthorized_connection
      end
    end

    def session
      cookies.encrypted[Rails.application.config.session_options[:key]]
    end
  end
end
chatroom_channel.rb
# (省略)

  def speak(data)
    message = Post.create! content: data['message'], user_id: data['user_id'], chatroom_id: data['room_id']
    templete = ApplicationController.renderer.render(partial: 'posts/post', locals: { post: message, current_user: current_user }) # current_user変数にconnection.rbで取得したcurrent_userを設定
    ActionCable.server.broadcast 'chatroom_channel', message: templete
  end

参考リンク

Rails5のActionCableで簡易チャットの作成 ~モデルに応じたチャンネルを聴講する方法~

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

【Rails】掲示板に禁止ワードを設定する

掲示板にクローラー的なやつがリンクやスクリプトを書き込み始めたので、暫定的な対応をしました。

comments_controller.rb
def create
 #以下の4行で対応
  prohibited_words = ['a href=', '<script']
  if prohibited_words.any? { |p| comment_params[:content].include?(p) }
    redirect_to(comments_url) && return
  end
  @comment = Comment.new(comment_params)
  if @comment.save
.
.

説明不要かもですが、まずprohibited_words = ['a href=', '<script']で禁止ワードを設定して、prohibited_words.any? { |p| <検証したい文字列>.include?(p) }で検証したい文字列に禁止ワードが含まれているかをチェックしています。

p.s.
掲示板荒らし対策に詳しい方、ご連絡ください。

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

リソースとは

リソースについて調べたこと

・リソースとはWebページ上の全てのデータのことをいう。
・リソースには読み込むことしかできないものや削除や更新ができるものなど制限が付いているものもある。
例 ツイッターのアイコンや投稿など

modelがリソース

railsのmodelはまさしくリソース(データ)で操作対象であるため、開発時には
1, model
2, Router
3,controller
4,view
の順で書いていくことを基本にして開発していく。慣れてきたら自分独自のやり方が見つかり変わるらしい。

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

Railsチュートリアル 第5章

ヘルパーメソッド

link_to

リンクを生成するためのヘルパーメソッド

<%= link_to "sample app", '#', id: "logo" %>

第1引数:リンクテキスト
第2引数:URL
第3引数:オプションハッシュ(必須ではない)

image_tag

画像を表示するためのimgタグを作成するヘルパーメソッド。

image_tag("rails.png", alt: "Rails logo")

第1引数:画像ファイル名
    ファイルの置き場所は「app/assets/images」フォルダと「public」フォルダの2つが用意されている。
上記の様に指定すると、「app/assets/images」が参照される。
    「public」フォルダを参照したい場合は、ファイル名の前に「/」を入れる。
第2引数:オプション
   alt:画像がない場合に代わりに表示される文字列
   size:'100×200' 幅と高さを指定

<%= link_to image_tag("rails.png", alt: "Rails logo"),
            'http://rubyonrails.org/' %>

上記の様に組み合わせて使うことができ、画像リンクが作れる
第1引数:image_tag("rails.png", alt: "Rails logo") ⇨画像
第2引数:'http://rubyonrails.org/'          ⇨URL

Bootstrap

Bootstrapとは

Twitter社が開発したCSSのフレームワークです。(HTML/CSS/JavaScriptから構成される)
よく使うスタイルなどがあらかじめ定義されている。
また、レスポンシブWebデザインに対応しているので、スマートフォンやタブレットなど個別に対応するスタイルを作らなくても柔軟に調整してくれる。

BootstrapではLESS CSS言語を使っているが、RailsのAsset PipelineはデフォルではSass言語をサポートしているので、bootstrap-sassは、LESSをSassへ変換し、Bootstrapファイルを現在のアプリケーションで全て利用できるようにする。

Sass

CSSを拡張した言語、元々のCSSに次の機能を追加

・ネスト
  スタイルシート内に共通のパターンがある場合、要素をネストさせることができる
・変数
冗長なコードを削除し、より自由な表現を可能に
・ミックスイン
 CSSルールのパッケージ化して、複数の箇所で再利用することができる (例:@include

Bootstrap導入

Gemfile.rb
source 'https://rubygems.org'

gem 'rails',          '5.1.6'
gem 'bootstrap-sass', '3.3.7' ⇦追加
.
.

Bootstrapをインストール

$ bundle install

カスタムCSSを作成

$ touch app/assets/stylesheets/custom.scss
custome.scss
@import "bootstrap-sprockets";
@import "bootstrap";

パーシャル

一箇所にまとめた方が良いHTMLや重複しているHTMLをパーシャルという機能を使って1つにまとめることができる。
パーシャルのファイル名の先頭にはアンダースコアをつける。
またrenderメソッドで使用する。

アセットパイプライン

CSS/JavaScript/Imageなどを管理する機能

アセットディレクトリ

静的ファイルを目的別に分類

  • app/assets:現在のアプリケーション固有のアセット
  • lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
  • vendor/assets: サードパーティのアセット

マニフェストファイル

1つのファイルにまとめる方法をRailsに指示

app/assets/stylesheets/application.css
/*
 * This is a manifest file that'll be compiled into application.css, which
 * will include all the files listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets,
 * vendor/assets/stylesheets, or vendor/assets/stylesheets of plugins, if any,
 * can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear
 * at the bottom of the compiled file so the styles you add here take
 * precedence over styles defined in any styles defined in the other CSS/SCSS
 * files in this directory. It is generally better to create a new file per
 * style scope.
 *
 *= require_tree .
 *= require_self
 */

require_tree:app/assets/stylesheetsディレクトリ内の全てのCSSファイルがアプリケーションCSSに含まれるようにしている。
require_self:CSSの読み込みシーケンスの中で、application.css自身もその対象に含めている。

プリプロセッサエンジン

指示に従ってブラウザに配信できるように結合

なぜアセットパイプライン?

開発する側としては複数のファイルに分割して効率的に開発できる
ただ、コンピューターに取っては1つにまとまっていた方が高速になるので、それをしてくれるのがアセットパイプライン

名前付きルート

このようなルートは

get 'static_pages/help'

このように変更する

get  '/help', to: 'static_pages#help'

GETリクエストが/helpに送信されたときにStaticPagesコントローラーのhelpアクションを呼び出してくれるようになる。
また、ルートURLの時と同様に、help_pathやhelp_urlといった名前付きルートも使えるようになる。

asオプション

:asオプションを使うと、ルーティングの名前を指定できる。

get '/help', to: 'static_pages#help', as: :helf

helf_pathとして使用できる

リンクのテスト

require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest

  test "layout links" do
    get root_path                     ⇨ルートURL(Homeページ)にGETリクエストを送る
    assert_template 'static_pages/home'        ⇨正しいテンプレートが表示されている事を確認
    assert_select "a[href=?]", root_path, count: 2 ⇨Homeページへのリンクが2つ存在する事を確認
    assert_select "a[href=?]", help_path       ⇨Helpページへのリンクが存在する事を確認
    assert_select "a[href=?]", about_path       ⇨Aboutページへのリンクが存在する事を確認
    assert_select "a[href=?]", contact_path     ⇨Contactページへのリンクが存在する事を確認
  end
end

assert_selectにはいくつもの指定の方法があるが、今回はリンクのテストをする。

コード マッチするHTML
assert_select "div" <div>foobar</div>
assert_select "div", "foobar" <div>foobar</div>
assert_select "div.nav" <div class="nav">foobar</div>
assert_select "div#profile" <div id="profile">foobar</div>
assert_select "div[name=yo]" <div name="yo">hey</div>
assert_select "a[href=?]", ’/’, count: 1 <a href="/">foo</a>
assert_select "a[href=?]", ’/’, text: "foo" <a href="/">foo</a>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのテンプレファイル(html.erb)でのコメント方法

一度うまくいった記述を、きれいなコードにする際にコメントアウトして残す癖があるが、html.erbファイルでのコメント方法が分からず調べたため備忘録として。

  1. <%#= %>もしくは<%# %>で囲む
<%# ココはコメントアウトされる %>
<%#= ココはコメントアウトされる %>

複数行の場合は

<%#
ココは
コメントアウト
される
%>

 
2. <% if false %> <% end %>で囲む
できるっちゃできますが、コメントアウトというより正しくはデッドロジック作っているので、やはり無理やり感ありますね。

<% if false %>
ココはコメントアウトされる
<% end %>

その他

なお、他にも、複数行コメントとして、<% begin %><% end >%で囲むという方法も見つかりましたが、上記1.ほどシンプルでなく改行によってはうまく機能しなかったりするので、通常使うにはイマイチということで、記載していません。

 参考

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

あっさり読むrails 番外(image)

はじめに

以前の記事の応用で、画像アドレスをフォームに入力すると、自動的に画像が表示されるようにします。

前提

前記事をそのまま修正します。

実行

ビューファイルを次の様に修正します。

sample.haml.html
= form_with model:@sample, local: true do |f|
  = f.text_area :name, placeholder: "サンプル", class: "sample-form"

.add-image

add-textadd-imageに変えただけです。

JSファイルを次の様に書きます。

sample.js
$(function(){
 $(".sample-form").on("change" functon(){
   var image = $(this).val();
   let html = `
     <div class = "add-image__sample">
       <img src = "$(image)" height = "500" //heightはなんでもいい
     </div>
   `
   ;
   $(".add-image").append(html);
 })
});

大雑把な流れとしては、
1.フォーム(.sample-form)の中身が変わった("change")時=画像アドレスを貼り付けた時
2.その値をimageに代入し
3.それを表示させるhtmlデータを変数に代入し
4..add-imageに追加(append)する

となります。

本当は、別の画像アドレスを貼り付ける時に元の画像を削除する必要がありますが、
ひとまずはこれで非同期の画像表示ができます。

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

メモ:ActionMailerのログにメールアドレスが出ないようにする

この記事は

  • 意外とぱっとできなかったので個人メモです

やりたかった事

  • ActionMailerでメールを送信する際に、下記のようにログにメールアドレスが出てしまうのですが、セキュリティのレギュレーション上、これを止めたかったです
INFO -- : [ActiveJob] [ActionMailer::DeliveryJob] Sent mail to test@example.com (95.4ms)

やり方

  • Rails側で設定はできません
  • モンキーパッチあてればいい説があり、やってみたのですが、これが全く反映されない
  • 結局、下記のようにLogSubscriberのサブクラスを作って、ActionMailerが読み込まれたあとでattachしなおしたら反映されました
config/initializers/log_subscriber_ext.rb
ActiveSupport.on_load(:action_mailer) do
  class LogSubscriberExt < ActionMailer::LogSubscriber
    def deliver(event)
      info do
        recipients = Array(event.payload[:to]).join(", ")
        "Sent mail to [FILTERED] (#{event.duration.round(1)}ms)"
      end

      debug { event.payload[:mail] }
    end
  end  
  LogSubscriberExt.attach_to :action_mailer
end
  • ちなみにメソッドの中身は/actionmailer-5.2.0/lib/action_mailer/log_subscriber.rbをコピーしてFILTER文字列で潰しただけです
  • Railsのバージョンによって中身が異なるかもしれないので注意

終わりに

  • Web側はfilter_parameter_loggingみたいな仕組みがあるんだから、こっちにも準備して欲しいなー

参考

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

rails install で詰まった話

結論この記事を参考にしたら解決

https://qiita.com/amuyikam/items/313bc89c1de320a4257e

こんなエラーが

➜  ~ gem install rails
ERROR:  Error installing rails:
    zeitwerk requires Ruby version >= 2.4.4.

rubyのバージョンが古かった。

  ~ ruby -v
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin18]

この記事を参考にrubyのバージョンを 2.4以上にする

https://qiita.com/TAByasu/items/47c6cfbeeafad39eda07

ダメか

➜  ~ rails new sample
Rails is not currently installed on this system. To get the latest version, simply type:

    $ sudo gem install rails

You can then rerun your "rails" command.
# これで解決
hash -r
➜  ~ rails _5.2.2.1_ new sample
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ミニQ & Aサービスの開発

Udemyの教材でミニQ&Aサービスの開発について学んだので、備忘録として記録します。
教材:https://www.udemy.com/course/the-ultimate-ruby-on-rails-bootcamp/

Rubyのバージョン管理

今回はバージョン2.5.1で進めました。

ターミナル
$ rvm -v #rvmのバージョンを調べる
$ rvm 1.29.8 (1.29.8) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]

$ rvm list
=* ruby-2.5.1 [ x86_64 ]
   ruby-2.6.3 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

$ rvm install 2.5.1 #バージョンを指定してインストールしたい場合

$ rvm use 2.5.1  #currentになる
$ rvm --default use 2.5.1  #current && defaultになる

Railsのインストール

バージョンを指定する場合の書き方。-Nはドキュメントをインストールしないという意味で時間短縮になる。

ターミナル
$ gem install rails -v 5.2.1 -N

Bundler RubyGemsを管理するツール

RubyGems Rubyで書かれたサードパーティ製のライブラリ(プログラムのまとまり)
ex) ユーザー認証機能、画像の管理機能、管理画面の機能、など
http://rubygems.org/

ミニQ and Aアプリ作成

ターミナル
$ rails _5.2.1_ new qanda
Gemfile
#エラー回避
#変更前
gem 'sqlite3'
#変更後
gem 'sqlite3', '~> 1.3.6'
ターミナル
$ bundle update

データベースを作成する

ターミナル
rails db:create

Bootstrapの導入

Gemfile
gem 'bootstrap', '~> 4.1.1'
gem 'jquery-rails', '~> 4.3.1' #bootstrapはjQueryに依存している
ターミナル
$ bundle install

stylesheetのファイル形式変更(CSS→Sass)

ターミナル
$ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss
application.scss
@import "bootstrap";
application.js
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3 #ここを追記
//= require popper #ここを追記
//= require bootstrap-sprockets #ここを追記
//= require_tree .

route

ルーティングの設定

ターミナル
$ rails routes

Prefix Verb   URI Pattern                                                                              Controller#Action
                     root GET    /                                                                                        questions#index
         question_answers GET    /questions/:question_id/answers(.:format)                                                answers#index
                          POST   /questions/:question_id/answers(.:format)                                                answers#create
      new_question_answer GET    /questions/:question_id/answers/new(.:format)                                            answers#new
     edit_question_answer GET    /questions/:question_id/answers/:id/edit(.:format)                                       answers#edit
          question_answer GET    /questions/:question_id/answers/:id(.:format)                                            answers#show
                          PATCH  /questions/:question_id/answers/:id(.:format)                                            answers#update
                          PUT    /questions/:question_id/answers/:id(.:format)                                            answers#update
                          DELETE /questions/:question_id/answers/:id(.:format)                                            answers#destroy
                questions GET    /questions(.:format)                                                                     questions#index
                          POST   /questions(.:format)                                                                     questions#create
             new_question GET    /questions/new(.:format)                                                                 questions#new
            edit_question GET    /questions/:id/edit(.:format)                                                            questions#edit
                 question GET    /questions/:id(.:format)                                                                 questions#show
                          PATCH  /questions/:id(.:format)                                                                 questions#update
                          PUT    /questions/:id(.:format)                                                                 questions#update
                          DELETE /questions/:id(.:format)                                                                 questions#destroy
       rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
       rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
     rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create
config/routes.rb
Rails.application.routes.draw do  
  root 'questions#index' #indexアクションのビューをrootに設定されるようする
  resources :questions do #questionsコントローラーに関係するルーティングを自動で用意してくれる
    resources :answers
  end
end

model

table.jpg

Questionモデルの作成

ターミナル
$ rails g model Question name:string title:string content:text
$ rails db:migrate

Answerモデルの作成

ターミナル
$ rails g model Answer question:references name:string content:text #question:references→1対多の関係を定義出来る
$ rails db:migrate

データベース構造の確認

ターミナル
$ rails dbconsole
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .schema #.schemaと入力
CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
CREATE TABLE "questions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "title" varchar, "content" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
CREATE TABLE "answers" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "question_id" integer, "name" varchar, "content" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, CONSTRAINT "fk_rails_3d5ed4418f"
FOREIGN KEY ("question_id")
  REFERENCES "questions" ("id")
);
CREATE INDEX "index_answers_on_question_id" ON "answers" ("question_id");
sqlite> .q #.qと入力
question.rb
class Question < ApplicationRecord
  has_many :answers, dependent: :destroy #questionは複数のanswerを持っているという意味
  #dependent: :destroy→親のquestionが削除されたら、それに紐づくanswersも全て削除される

  validates :name, presence: true #データが未入力の時に保存しないようにする
  validates :title, presence: true
  validates :content, presence: true
end
answer.rb
class Answer < ApplicationRecord
  belongs_to :question #answerはquestionによって所有されていると言う関係

  validates :content, presence: true
  validates :name, presence: true
end

controller

ターミナル
$ rails g controller questions index show new edit #questionsコントローラーのindex,show,new,editアクションを作成
question_controller.rb
class QuestionsController < ApplicationController

  #コントローラーの各アクションが実行される前にset_questionを実行する
  before_action :set_question, only: [:show, :edit, :update, :destroy]


  #一覧を表示する
  def index
    #Questionの全データの配列を取得して、@questionsインスタンス変数へ代入する
    @questions = Question.all
  end

  #詳細を表示する
  def show
    # @question = Question.find(params[:id]) #リファクタリング
    @answer = Answer.new #Answerクラスのインスタンスを作り、@answerインスタンス変数へ代入
  end

  #新規作成
  def new
    @question = Question.new #Questionモデルの空のインスタンスを作り、@questionインスタンス変数へ代入
  end

  #保存
  def create
    @question = Question.new(question_params) #name, title, contentを@questionに代入
    if @question.save #保存されたら
      redirect_to root_path, notice: 'Success!' #「Success!」と表示してルートパスにリダイレクトする
    else #保存できない場合は
      flash[:alert] = "Save error!" #「Save error!」と表示して
      render :new #新規ページに戻る
    end
  end

  #編集
  def edit
    # @question = Question.find(params[:id]) #リファクタリング
  end

  #アップデート
  def update
    # @question = Question.find(params[:id]) #リファクタリング
    if @question.update(question_params) #name, title, contentをアップデートできたら
      redirect_to root_path, notice: 'Success!' #「Success!」と表示してルートパスにリダイレクトする
    else #アップデートできない場合は
      flash[:alert] = "Save error!" #「Save error!」と表示して
      render :edit #編集ページに戻る
    end
  end

  def destroy
    # @question = Question.find(params[:id]) #リファクタリング
    @question.destroy #削除する
    redirect_to root_path, notice: 'Success!' #「Success!」と表示してルートパスにリダイレクトする
  end

  private
  def set_question #@question = Question.find(params[:id])が重複しているので共通化する!
    @question = Question.find(params[:id])  #(params[:id])を取得して@questionに代入 
  end

  #ストロングパラメーター
  private #クラスの内部だけで使用する
  def question_params
    params.require(:question).permit(:name, :title, :content) #name,title,contentのみの値を@questionに渡す
  end
end

★paramsに入るデータをデバッグツールbyebugで確認する
formに値を入れて、ターミナルでparamsを入力するとフォームから送られてきた値を確認することができる
終了するには、quitを実行する

question_controller.rb
private
def question_params
  byebug #byebugを入力
  params.require(:question).permit(:name, :title, :content) #paramsにはフォームから送られてきたデータが入る
end
ターミナル
$ rails g controller answers edit  #answersコントローラーのeditアクションを作成
answers_controller.rb
class AnswersController < ApplicationController

  #保存
  def create
    #question_idを元にデータベースから該当のquestionを取得して変数に代入する
    @question = Question.find(params[:question_id]) 

    #Answerモデルの空のインスタンスを作り、@answerインスタンス変数へ代入
    @answer = Answer.new 

    #content,name,question_idを保存できたら
    if @answer.update(answer_params) 

      #「Success!」と表示して詳細ページににリダイレクトする
      redirect_to question_path(@question), notice: 'Success!'

    #アップデートできない場合は
    else 
      #「Invalid!」と表示して詳細ページににリダイレクトする
      redirect_to question_path(@question), alert: 'Invalid!' 
    end
  end

  def edit
    #question_idを元にデータベースから該当のquestionを取得して変数に代入する
    @question = Question.find(params[:question_id]) 

    #取得した@questionから編集したいanswerのidを取得して@answerに代入する
    @answer = @question.answers.find(params[:id]) 
  end

  def update
    #question_idを元にデータベースから該当のquestionを取得して変数に代入する
    @question = Question.find(params[:question_id])

    #取得した@questionからアップデートしたいanswerのidを取得して@answerに代入する
    @answer = @question.answers.find(params[:id])

    #アップデートできたら
    if @answer.update(answer_params) 

      #「Success!」と表示して詳細ページににリダイレクトする
      redirect_to question_path(@question), notice: 'Success!'

    #アップデートできない場合は
    else
      #「Invalid!」と表示して
      flash[:alert] = 'Invalid!' 
      #編集ページに戻る
      render :edit 
    end
  end

  def destroy
    #question_idを元にデータベースから該当のquestionを取得して変数に代入する
    @question = Question.find(params[:question_id]) 
    #取得した@questionから編集したいanswerのidを取得して@answerに代入する
    @answer = @question.answers.find(params[:id])
    #削除する
    @answer.destroy 
    #「Deleted!」と表示して詳細ページににリダイレクトする
    redirect_to question_path(@question), notice: 'Deleted!' 
  end

  #ストロングパラメーター
  private
  def answer_params
    #content,name,question_idのみの値を@answerに渡す
    params.require(:answer).permit(:content, :name, :question_id) 
  end
end

view

questions/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Qanda</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <div class="container">
      <% if flash[:notice] %>
        <p class="text-success"><%= flash[:notice] %></p>
      <% end %>
      <% if flash[:alert] %>
        <p class="text-danger"><%= flash[:alert] %></p>
      <% end %>
      <%= yield %>
    </div>
  </body>
</html>

質問一覧ページの作成

questions/index.html.erb
<h2>Questions</h2>
<div class="row">
  <div class="col-md-12">
    <table class="table table-striped">
      <thead class="thead-light">
        <tr>
          <th>ID</th>
          <th>Title</th>
          <th>Menu</th>
        </tr>
      </thead>
      <tbody>
        <% @questions.each do |question| %>
          <tr>
            <td><%= question.id %></td>
       <!-- 質問詳細画面へのリンク -->
            <!-- prefixに_pathをつけている -->
            <!-- (question)→:idとして読み込まれる -->
            <!-- question GET    /questions/:id(.:format)   questions#show -->
            <td><%= link_to question.title, question_path(question) %></td>

            <!-- edit_question GET    /questions/:id/edit(.:format)    questions#edit -->
            <td>[<%= link_to 'Edit', edit_question_path(question) %>]
            <!-- question DELETE /questions/:id(.:format)  questions#destroy -->
            [<%= link_to 'Delete', question_path(question), method: :delete, data:{ confirm: 'Are you sure?' }  %>]</td>
          </tr>
        <% end %>
      </tbody>
    </table>
    <div>
    <!--  new_question GET  /questions/new(.:format) questions#new -->
      <%= link_to 'New question', new_question_path %>
    </div>
  </div>
</div>

シードファイルを使った初期データの投入

db/seeds.rb
Question.create(id: 1, name: 'Test name 1', title: 'Test question 1', content: 'Test content 1')
Question.create(id: 2, name: 'Test name 2', title: 'Test question 2', content: 'Test content 2')
Question.create(id: 3, name: 'Test name 3', title: 'Test question 3', content: 'Test content 3')
rails db:seed

新規質問投稿ページの作成

questions/new.html.erb
<div>
  <div class="col-md-4 offset-md-4">
    <h2 class="text-center">New question</h2>
    <%= render 'form' %> <!--_form.html.erbを読み込む -->
  </div>
</div>

質問編集ページの作成

questions/edit.html.erb
<div>
  <div class="col-md-4 offset-md-4">
    <h2 class="text-center">Edit question</h2>
    <%= render 'form' %> <!--_form.html.erbを読み込む -->
  </div>
</div>

共通部分の作成

リファクタリング:プログラムを外部から見た時に動作を変えずに、ソースコードを整理すること
new.html.erbとedit.html.erbはほぼ同じ内容になっている
共通部分を_form.html.erbで作成し、new.html.erbとedit.html.erbで読み込む

questions/_form.html.erb
<%= form_with model: @question, local: true do |f| %>
  <div class="form-group">
    <label>Name</label>
    <%= f.text_field :name, class: "form-control" %>
  </div>

  <div class="form-group">
    <label>Title</label>
    <%= f.text_field :title, class: "form-control" %>
  </div>

  <div class="form-group">
    <label>Content</label>
    <%= f.text_area :content, class: "form-control" %>
  </div>

  <div class="text-center">
    <%= f.submit "Save", class: "btn btn-primary" %>
  </div>

<% end %>

質問詳細ページの作成

questions/show.html.erb
<div class="row">
  <div class="col-md-12">
    <h2><%= @question.title %></h2> <!-- 質問のタイトル -->
    <div>
      Content: <%= @question.content %> <!-- 質問の内容 -->
    </div>
    <div>
      Name: <%= @question.name %> <!-- 名前 -->
    </div>
    <hr>

    <div>
      <h3>Answers</h3>
      <table class="table table-striped">
        <% if @question.answers.any? %> <!-- 質問の答えがある場合は下記を表示する -->
          <thead class="thead-light">
            <tr>
              <td>Answer</td>
              <td>Name</td>
              <td>Menu</td>
            </tr>
          </thead>
          <tbody>
            <% @question.answers.each do |answer| %>
            <tr>
              <td>
                <%= answer.content %> <!-- 答えの内容 -->
              </td>
              <td>
                <%= answer.name %> <!-- 名前 -->
              </td>
              <td>
          <!-- edit_question_answer GET /questions/:question_id/answers/:id/edit(.:format)answers#edit -->
                [<%= link_to 'Edit', edit_question_answer_path(@question, answer) %>]

                <!-- question_answer DELETE /questions/:question_id/answers/:id(.:format) answers#destroy -->
                [<%= link_to 'Delete', question_answer_path(@question, answer), method: :delete, data: {confirm: 'Are you sure?'} %> ]
              </td>
            </tr>
            <% end %>
          </tbody>
        <% else %>
        <p>No answer yet.</p> <!-- 質問の答えがない場合に表示する -->
        <% end %>
      </table>
    </div>

    <h3>Post new answer</h3>
    <%= form_with model: [@question, @answer], local: true do |f| %>
      <%= f.hidden_field :question_id, { value: @question.id} %>
      <div class="form-group">
        <label>Name</label>
        <%= f.text_field :name, class: 'form-control' %>
      </div>
      <div class="form-group">
        <label>Content</label>
        <%= f.text_area :content, class: 'form-control' %>
      </div>
      <div class="text-center">
        <%= f.submit "Post", class: 'btn btn-primary' %>
      </div>
    <% end %>

    <div>
      <%= link_to '> Home', root_path %> <!-- ルートパスに飛ぶ -->
    </div>
  </div>
</div>

<% end %>

回答編集ページの作成

answers/edit.html.erb
<div>
  <h2>Updates answer</h2>
  <%= form_with model:[@question, @answer], local: true do |f| %>
    <div class="form-group">
      <div class="form-group">
        <label>Name</label>
        <%= f.text_field :name, class: "form-control" %>
      </div>
      <div class="form-group">
        <label>Content</label>
        <%= f.text_area :content, class: "form-control" %>
      </div>
      <div class="text-center">
        <%= f.submit "Update", class: "btn btn-primary" %>
      </div>
    </div>
  <% end %>
</div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【解決策】formにカラムが設定されない!?

Webアプリを開発していて、routesとcontroller、viewを何度も見直したのにformを検証で見てみると何故かカラムが設定されない時に以下の方法で解決しました。

before

  = form_with model: @message, url: job_messages_path(@job), local: true do |f|
    = f.text_field :text

after

  = form_with model: @message, url: job_messages_path(@job), local: true do |f|
    = f.text_field :text, name: "message[text]"

下の行に、name:"テーブル名[カラム名]"を書き足せばカラムが設定され、値を入れることができました!!

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

Active Storage + S3を使うときに気をつけること

備忘録なので無視してください

file_fieldで写真をアップする時にdirect_uploadを設定しないとNo route matchesになる時がある。

= f.file_field :image, direct_upload: true
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む